per-file-ignores =
# for init_test_paths() hack
*_test_*.py:E402
+ *_test.py:E402
+ *bug_*.py:E402
+ test_*.py:E402
+ signal_across_threads.py:E402
__init__.py:F401,E402
-
--- /dev/null
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
+ 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
+ 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
+ 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
+ (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
+ (b) You must cause any modified files to carry prominent notices stating that You changed the files; and
+ (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
+ (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
+ 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
+ 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
+ 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
+ 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
+ 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
## Requirements
- * Python 3.8+ is supported (for Qt 6.6+)
+ * Python 3.9+ is supported (for Qt 6.7+)
* CMake: Specify the path to cmake with `--cmake` option or add cmake to the
system path.
* Qt 6.x is supported. Specify the path to qtpaths with `--qtpaths` option or
### Licensing
-PySide6 is available under both Open Source (LGPLv3/GPLv2) and commercial
+PySide6 is available under both Open Source (LGPLv3/GPLv3) and commercial
license. Using PyPi is the recommended installation source, because the
content of the wheels is valid for both cases. For more information, refer to
the [Qt Licensing page](https://www.qt.io/licensing/).
### Licensing
-PySide6 is available under both Open Source (LGPLv3/GPLv2) and commercial
+PySide6 is available under both Open Source (LGPLv3/GPLv3) and commercial
license. Using PyPi is the recommended installation source, because the
content of the wheels is valid for both cases. For more information, refer to
the [Qt Licensing page](https://www.qt.io/licensing/).
* QtQml
* QtQuick
* QtQuickControls2
+* QtQuickTest
* QtQuickWidgets
* QtXml
* QtTest
### Licensing
-PySide6 is available under both Open Source (LGPLv3/GPLv2) and commercial
+PySide6 is available under both Open Source (LGPLv3/GPLv3) and commercial
license. Using PyPi is the recommended installation source, because the
content of the wheels is valid for both cases. For more information, refer to
the [Qt Licensing page](https://www.qt.io/licensing/).
### Licensing
-PySide6 is available under both Open Source (LGPLv3/GPLv2) and commercial
+PySide6 is available under both Open Source (LGPLv3/GPLv3) and commercial
licenses. Using PyPi is the recommended installation source, because the
content of the wheels is valid for both cases. For more information, refer to
the [Qt Licensing page](https://www.qt.io/licensing/).
"lrelease",
"qmllint",
"qmlformat",
- "qmlls"]
+ "qmlls",
+ "qsb",
+ "balsam",
+ "balsamui"]
# tools that are bundled as .app in macOS, but are normal executables in Linux and Windows
PYSIDE_UNIX_BUNDLED_TOOLS = ["assistant",
if hasattr(sys, "pypy_version_info"):
vi = sys.version_info[:2]
version_quirk = ".".join(map(str, vi)) if vi >= (3, 9) else "3"
- pypy_libdir = Path(py_libdir).parent / "bin"
+ pypy_libdir = Path(py_libdir).parent / "bin"
for lib_ext in lib_exts:
lib_name = f"libpypy{version_quirk}-c{lib_ext}"
pypy_library = pypy_libdir / lib_name
self.python_version_classifiers = [
'Programming Language :: Python',
'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
setup_kwargs['zip_safe'] = False
setup_kwargs['cmdclass'] = cmd_class_dict
setup_kwargs['version'] = package_version
- setup_kwargs['python_requires'] = ">=3.8, <3.13"
+ setup_kwargs['python_requires'] = ">=3.9, <3.13"
if log_level == LogLevel.QUIET:
# Tells setuptools to be quiet, and only print warnings or errors.
logging.basicConfig(format="[%(levelname)s]: %(message)s", level=logging.INFO)
log = logging.getLogger("qtforpython")
+
class LogLevel(Enum):
QUIET = 1
INFO = 2
VERBOSE = 3
-
import time
from packaging.version import parse as parse_version
from pathlib import Path
-from shutil import copytree
+from shutil import copytree, rmtree
from textwrap import dedent
# PYSIDE-1760: Pre-load setuptools modules early to avoid racing conditions.
else:
raise SetupError("option limited-api must be 'yes' or 'no' "
"(default yes if applicable, i.e. Python "
- "version >= 3.8 and release build if on Windows)")
+ "version >= 3.9 and release build if on Windows)")
if OPTION["DISABLE_PYI"]:
cmake_cmd.append("-DDISABLE_PYI=yes")
cmake_cmd.append(f"-DPACKAGE_SETUP_PY_PACKAGE_TIMESTAMP={timestamp}")
if extension.lower() in [SHIBOKEN]:
- cmake_cmd.append("-DUSE_PYTHON_VERSION=3.8")
+ cmake_cmd.append("-DUSE_PYTHON_VERSION=3.9")
cmake_cmd += platform_cmake_options()
os.environ['MACOSX_DEPLOYMENT_TARGET'] = deployment_target
if OPTION["BUILD_DOCS"]:
- # Build the whole documentation (rst + API) by default
+ # Build the whole documentation (Base + API) by default
cmake_cmd.append("-DFULLDOCSBUILD=1")
if OPTION["DOC_BUILD_ONLINE"]:
log.debug(f"Patched rpath to '{rpath_value}' in {library}.")
-class PysideRstDocs(Command, CommandMixin):
- description = "Build .rst documentation only"
+class PysideBaseDocs(Command, CommandMixin):
+ description = "Build the base documentation only"
user_options = CommandMixin.mixin_user_options
def __init__(self, *args, **kwargs):
- self.command_name = "build_rst_docs"
+ if args[0].commands[0] == "build_rst_docs":
+ args[0].commands[0] = "build_base_docs"
+ log.warning("'build_rst_docs' is deprecated and will be removed. "
+ "Please use 'build_base_docs' instead.")
+ self.command_name = "build_base_docs"
Command.__init__(self, *args, **kwargs)
CommandMixin.__init__(self)
def initialize_options(self):
- log.info("-- This build process will not include the API documentation."
+ log.info("-- This build process will not include the API documentation. "
"API documentation requires a full build of pyside/shiboken.")
self.skip = False
if config.is_internal_shiboken_generator_build():
self.doc_dir = config.setup_script_dir / "sources" / self.name / "doc"
# Check if sphinx is installed to proceed.
found = importlib.util.find_spec("sphinx")
+ self.html_dir = Path("html")
if found:
if self.name == SHIBOKEN:
+ # Delete the 'html' directory since new docs will be generated anyway
+ if self.html_dir.is_dir():
+ rmtree(self.html_dir)
+ log.info("-- Deleted old html directory")
log.info("-- Generating Shiboken documentation")
log.info(f"-- Documentation directory: 'html/{PYSIDE}/{SHIBOKEN}/'")
elif self.name == PYSIDE:
log.info(f"-- Documentation directory: 'html/{PYSIDE}/'")
else:
raise SetupError("Sphinx not found - aborting")
- self.html_dir = Path("html")
# creating directories html/pyside6/shiboken6
try:
raise SetupError(f"Error running CMake for {self.doc_dir}")
if self.name == PYSIDE:
- self.sphinx_src = self.out_dir / "rst"
+ self.sphinx_src = self.out_dir / "base"
example_gallery = config.setup_script_dir / "tools" / "example_gallery" / "main.py"
assert example_gallery.is_file()
example_gallery_cmd = [sys.executable, os.fspath(example_gallery)]
'develop': PysideDevelop,
'install': PysideInstall,
'install_lib': PysideInstallLib,
- 'build_rst_docs': PysideRstDocs,
+ 'build_base_docs': PysideBaseDocs,
+ # TODO: Remove build_rst_docs in the next version, see PYSIDE-2504
+ 'build_rst_docs': PysideBaseDocs,
}
if wheel_module_exists:
pyside_bdist_wheel = get_bdist_wheel_override()
def find_qtpaths():
# for these command --qtpaths should not be required
- no_qtpaths_commands = ["--help", "--help-commands", "--qt-target-path", "build_rst_docs"]
+ no_qtpaths_commands = ["--help", "--help-commands", "--qt-target-path", "build_base_docs",
+ "build_rst_docs"]
for no_qtpaths_command in no_qtpaths_commands:
if any(no_qtpaths_command in argument for argument in sys.argv):
# We redeclare plat-name as an option so it's recognized by the
# install command and doesn't throw an error.
('plat-name=', None, 'The platform name for which we are cross-compiling'),
- ('unity', None, 'Use CMake UNITY_BUILD_MODE'),
+ ('unity', None, 'Use CMake UNITY_BUILD_MODE (obsolete)'),
+ ('no-unity', None, 'Disable CMake UNITY_BUILD_MODE'),
('unity-build-batch-size=', None, 'Value of CMAKE_UNITY_BUILD_BATCH_SIZE')
]
self.internal_cmake_install_dir_query_file_path = None
self._per_command_mixin_options_finalized = False
self.unity = False
+ self.no_unity = False
self.unity_build_batch_size = "16"
# When initializing a command other than the main one (so the
OPTION['NO_STRIP'] = self.no_strip
OPTION['ONLYPACKAGE'] = self.only_package
OPTION['STANDALONE'] = self.standalone
- OPTION['IGNOREGIT'] = self.ignore_git
+ if self.ignore_git:
+ _warn_deprecated_option('ignore_git')
OPTION['SKIP_DOCS'] = self.skip_docs
OPTION['BUILD_DOCS'] = self.build_docs
OPTION['BUILDTESTS'] = self.build_tests
# By default they are False, so we check if they changed with xor
if bool(OPTION["QUIET"]) != bool(OPTION["VERBOSE_BUILD"]):
- log.warn("Using --quiet and --verbose-build is deprecated. "
- "Please use --log-level=quiet or --log-level=verbose instead.")
+ log.warning("Using --quiet and --verbose-build is deprecated. "
+ "Please use --log-level=quiet or --log-level=verbose instead.")
# We assign a string value instead of the enum
# because is what we get from the command line.
# Later we assign the enum
OPTION['SANITIZE_ADDRESS'] = self.sanitize_address
OPTION['SHORTER_PATHS'] = self.shorter_paths
OPTION['DOC_BUILD_ONLINE'] = self.doc_build_online
- OPTION['UNITY'] = self.unity
+ if self.unity:
+ log.warning("Using --unity no longer has any effect, "
+ "Unity build mode is now the default.")
+ OPTION['UNITY'] = not self.no_unity
OPTION['UNITY_BUILD_BATCH_SIZE'] = self.unity_build_batch_size
qtpaths_abs_path = None
qt_target_path=qt_target_path,
cmake_toolchain_file=cmake_toolchain_file)
- if 'build_rst_docs' not in sys.argv:
+ if 'build_base_docs' not in sys.argv and 'build_rst_docs' not in sys.argv:
try:
QtInfo().prefix_dir
except Exception as e:
# We also don't do auto-searching if qt-target-path is passed
# explicitly. This is to help with the building of host tools
# while cross-compiling.
- # Skip this process for the 'build_rst_docs' command
- if not (self.is_cross_compile and not self.qt_target_path
+ # Skip this process for the 'build_base_docs' command
+ if (not self.is_cross_compile
+ and not self.qt_target_path
+ and 'build_base_docs' not in sys.argv
and 'build_rst_docs' not in sys.argv):
# Enforce usage of qmake in QtInfo if it was given explicitly.
if self.qmake:
_filter=["*.so"],
recursive=True,
_vars=_vars)
- if not is_pypy:
+ if not is_pypy and not is_android:
copydir("{install_dir}/plugins/designer",
plugins_target / "designer",
_filter=["*.so"],
scripts = ["pyside_tool.py", "metaobjectdump.py", "project.py", "qml.py",
"qtpy2cpp.py", "deploy.py"]
- script_dirs = ["qtpy2cpp_lib", "deploy_lib", "project"]
+ script_dirs = ["qtpy2cpp_lib", "deploy_lib", "project"]
if sys.platform.startswith("linux"):
scripts.append("android_deploy.py")
lib_exec_filters.append('QtWebEngineProcess')
if lib_exec_filters:
libexec_executables.extend(copydir("{qt_lib_execs_dir}",
- destination_qt_dir / "libexec",
- _filter=lib_exec_filters,
- recursive=False,
- _vars=_vars))
+ destination_qt_dir / "libexec",
+ _filter=lib_exec_filters,
+ recursive=False,
+ _vars=_vars))
# <install>/lib/lib* -> {st_package_name}/
copydir(
# <source>/pyside6/{st_package_name}/QtAsyncio/* ->
# <setup>/{st_package_name}/QtAsyncio/*
copydir(
- f"{{site_packages_dir}}/{{st_package_name}}/QtAsyncio",
+ "{site_packages_dir}/{st_package_name}/QtAsyncio",
"{st_build_dir}/{st_package_name}/QtAsyncio",
_vars=_vars)
# <source>/pyside6/{st_package_name}/QtAsyncio/* ->
# <setup>/{st_package_name}/QtAsyncio/*
copydir(
- f"{{site_packages_dir}}/{{st_package_name}}/QtAsyncio",
+ "{site_packages_dir}/{st_package_name}/QtAsyncio",
"{st_build_dir}/{st_package_name}/QtAsyncio",
_vars=_vars)
# <qt>/bin/*.dll and Qt *.exe -> <setup>/{st_package_name}
qt_artifacts_permanent = [
+ "avcodec-60.dll",
+ "avformat-60.dll",
+ "avutil-58.dll",
+ "swresample-4.dll",
+ "swscale-7.dll",
"opengl*.dll",
"designer.exe",
"linguist.exe",
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import datetime
+import os
+import re
+import subprocess
+import sys
+import time
+import warnings
+from argparse import ArgumentParser, RawTextHelpFormatter
+from enum import Enum, auto
+from pathlib import Path
+from typing import List
+
+DESC = """
+Utility script for working with Qt for Python.
+
+Feel free to extend!
+
+Typical Usage:
+Update and build a repository: python qfp_tool -p -b
+
+qfp_tool.py uses a configuration file "%CONFIGFILE%"
+in the format key=value.
+
+It is possible to use repository-specific values by adding a key postfixed by
+a dash and the repository folder base name, eg:
+Modules-pyside-setup512=Core,Gui,Widgets,Network,Test
+
+Configuration keys:
+Acceleration Incredibuild or unset
+BuildArguments Arguments to setup.py
+Generator Generator to be used for CMake. Currently, only Ninja is
+ supported.
+Jobs Number of jobs to be run simultaneously
+Modules Comma separated list of modules to be built
+ (for --module-subset=)
+Python Python executable (Use python_d for debug builds on Windows)
+
+Arbitrary keys can be defined and referenced by $(name):
+
+MinimalModules=Core,Gui,Widgets,Network,Test
+Modules=$(MinimalModules),Multimedia
+Modules-pyside-setup-minimal=$(MinimalModules)
+"""
+
+
+class Acceleration(Enum):
+ NONE = 0
+ INCREDIBUILD = 1
+
+
+class BuildMode(Enum):
+ NONE = 0
+ BUILD = 1
+ RECONFIGURE = 2
+ MAKE = 3
+
+
+class UnityMode(Enum):
+ DEFAULT = auto()
+ ENABLE = auto()
+ DISABLE = auto()
+
+
+DISABLE_UNITY_OPTION = "--no-unity"
+LOG_LEVEL_OPTION = "--log-level"
+DEFAULT_BUILD_ARGS = ['--build-tests', '--skip-docs', LOG_LEVEL_OPTION, "quiet"]
+IS_WINDOWS = sys.platform == 'win32'
+INCREDIBUILD_CONSOLE = 'BuildConsole' if IS_WINDOWS else '/opt/incredibuild/bin/ib_console'
+# Config file keys
+ACCELERATION_KEY = 'Acceleration'
+BUILDARGUMENTS_KEY = 'BuildArguments'
+GENERATOR_KEY = 'Generator'
+JOBS_KEY = 'Jobs'
+MODULES_KEY = 'Modules'
+PYTHON_KEY = 'Python'
+
+DEFAULT_MODULES = "Core,Gui,Widgets,Network,Test,Qml,Quick,Multimedia,MultimediaWidgets"
+DEFAULT_CONFIG_FILE = f"Modules={DEFAULT_MODULES}\n"
+
+build_mode = BuildMode.NONE
+opt_dry_run = False
+opt_verbose = False
+opt_unity_mode = UnityMode.DEFAULT
+
+
+def which(needle: str):
+ """Perform a path search"""
+ needles = [needle]
+ if IS_WINDOWS:
+ for ext in ("exe", "bat", "cmd"):
+ needles.append(f"{needle}.{ext}")
+
+ for path in os.environ.get("PATH", "").split(os.pathsep):
+ for n in needles:
+ binary = Path(path) / n
+ if binary.is_file():
+ return binary
+ return None
+
+
+def command_log_string(args: List[str], directory: Path):
+ result = f'[{directory.name}]'
+ for arg in args:
+ result += f' "{arg}"' if ' ' in arg else f' {arg}'
+ return result
+
+
+def execute(args: List[str]):
+ """Execute a command and print to log"""
+ log_string = command_log_string(args, Path.cwd())
+ print(log_string)
+ if opt_dry_run:
+ return
+ exit_code = subprocess.call(args)
+ if exit_code != 0:
+ raise RuntimeError(f'FAIL({exit_code}): {log_string}')
+
+
+def run_process_output(args):
+ """Run a process and return its output. Also run in dry_run mode"""
+ std_out = subprocess.Popen(args, universal_newlines=1,
+ stdout=subprocess.PIPE).stdout
+ result = [line.rstrip() for line in std_out.readlines()]
+ std_out.close()
+ return result
+
+
+def run_git(args):
+ """Run git in the current directory and its submodules"""
+ args.insert(0, git) # run in repo
+ execute(args) # run for submodules
+
+
+def expand_reference(cache_dict, value):
+ """Expand references to other keys in config files $(name) by value."""
+ pattern = re.compile(r"\$\([^)]+\)")
+ while True:
+ match = pattern.match(value)
+ if not match:
+ break
+ key = match.group(0)[2:-1]
+ value = value[:match.start(0)] + cache_dict[key] + value[match.end(0):]
+ return value
+
+
+def editor():
+ editor = os.getenv('EDITOR')
+ if not editor:
+ return 'notepad' if IS_WINDOWS else 'vi'
+ editor = editor.strip()
+ if IS_WINDOWS:
+ # Windows: git requires quotes in the variable
+ if editor.startswith('"') and editor.endswith('"'):
+ editor = editor[1:-1]
+ editor = editor.replace('/', '\\')
+ return editor
+
+
+def edit_config_file():
+ exit_code = -1
+ try:
+ exit_code = subprocess.call([editor(), config_file])
+ except Exception as e:
+ reason = str(e)
+ print(f'Unable to launch: {editor()}: {reason}')
+ return exit_code
+
+
+"""
+Config file handling, cache and read function
+"""
+config_dict = {}
+
+
+def read_config_file(file_name):
+ """Read the config file into config_dict, expanding continuation lines"""
+ global config_dict
+ keyPattern = re.compile(r'^\s*([A-Za-z0-9\_\-]+)\s*=\s*(.*)$')
+ with open(file_name) as f:
+ while True:
+ line = f.readline()
+ if not line:
+ break
+ line = line.rstrip()
+ match = keyPattern.match(line)
+ if match:
+ key = match.group(1)
+ value = match.group(2)
+ while value.endswith('\\'):
+ value = value.rstrip('\\')
+ value += f.readline().rstrip()
+ config_dict[key] = expand_reference(config_dict, value)
+
+
+def read_config(key):
+ """
+ Read a value from the '$HOME/.qfp_tool' configuration file. When given
+ a key 'key' for the repository directory '/foo/qt-5', check for the
+ repo-specific value 'key-qt5' and then for the general 'key'.
+ """
+ if not config_dict:
+ read_config_file(config_file)
+ repo_value = config_dict.get(f"{key}-{base_dir}")
+ return repo_value if repo_value else config_dict.get(key)
+
+
+def read_bool_config(key):
+ value = read_config(key)
+ return value and value in ['1', 'true', 'True']
+
+
+def read_int_config(key, default=-1):
+ value = read_config(key)
+ return int(value) if value else default
+
+
+def read_acceleration_config():
+ value = read_config(ACCELERATION_KEY)
+ if value:
+ value = value.lower()
+ if value == 'incredibuild':
+ return Acceleration.INCREDIBUILD
+ return Acceleration.NONE
+
+
+def read_config_build_arguments():
+ value = read_config(BUILDARGUMENTS_KEY)
+ if value:
+ return re.split(r'\s+', value)
+ return DEFAULT_BUILD_ARGS
+
+
+def read_config_modules_argument():
+ value = read_config(MODULES_KEY)
+ if value and value != '' and value != 'all':
+ return f"--module-subset={value}"
+ return None
+
+
+def read_config_python_binary() -> str:
+ binary = read_config(PYTHON_KEY)
+ virtual_env = os.environ.get('VIRTUAL_ENV')
+ if not binary:
+ # Use 'python3' unless virtualenv is set
+ use_py3 = not virtual_env and which('python3')
+ binary = 'python3' if use_py3 else 'python'
+ binary = Path(binary)
+ if not binary.is_absolute():
+ abs_path = which(str(binary))
+ if abs_path:
+ binary = abs_path
+ else:
+ warnings.warn(f'Unable to find "{binary}"', RuntimeWarning)
+ if virtual_env:
+ if not str(binary).startswith(virtual_env):
+ w = f'Python "{binary}" is not under VIRTUAL_ENV "{virtual_env}"'
+ warnings.warn(w, RuntimeWarning)
+ return str(binary)
+
+
+def get_config_file(base_name) -> Path:
+ global user
+ home = os.getenv('HOME')
+ if IS_WINDOWS:
+ # Set a HOME variable on Windows such that scp. etc.
+ # feel at home (locating .ssh).
+ if not home:
+ home = os.getenv('HOMEDRIVE') + os.getenv('HOMEPATH')
+ os.environ['HOME'] = home
+ user = os.getenv('USERNAME')
+ config_file = Path(os.getenv('APPDATA')) / base_name
+ else:
+ user = os.getenv('USER')
+ config_dir = Path(home) / '.config'
+ if config_dir.exists():
+ config_file = config_dir / base_name
+ else:
+ config_file = Path(home) / f".{base_name}"
+ return config_file
+
+
+def build(target: str):
+ """Run configure and build steps"""
+ start_time = time.time()
+
+ arguments = []
+ acceleration = read_acceleration_config()
+ if not IS_WINDOWS and acceleration == Acceleration.INCREDIBUILD:
+ arguments.append(INCREDIBUILD_CONSOLE)
+ arguments.appendh('--avoid') # caching, v0.96.74
+ arguments.extend([read_config_python_binary(), 'setup.py', target])
+ build_arguments = read_config_build_arguments()
+ if opt_verbose and LOG_LEVEL_OPTION in build_arguments:
+ i = build_arguments.index(LOG_LEVEL_OPTION)
+ del build_arguments[i]
+ del build_arguments[i]
+ arguments.extend(build_arguments)
+ if opt_unity_mode != UnityMode.DEFAULT:
+ unity_disabled = DISABLE_UNITY_OPTION in build_arguments
+ if opt_unity_mode == UnityMode.ENABLE and unity_disabled:
+ arguments.remove(DISABLE_UNITY_OPTION)
+ elif opt_unity_mode == UnityMode.DISABLE and not unity_disabled:
+ arguments.append(DISABLE_UNITY_OPTION)
+ generator = read_config(GENERATOR_KEY)
+ if generator != 'Ninja':
+ arguments.extend(['--make-spec', 'ninja'])
+ jobs = read_int_config(JOBS_KEY)
+ if jobs > 1:
+ arguments.extend(['-j', str(jobs)])
+ if build_mode != BuildMode.BUILD:
+ arguments.append('--reuse-build')
+ if build_mode != BuildMode.RECONFIGURE:
+ arguments.append('--skip-cmake')
+ modules = read_config_modules_argument()
+ if modules:
+ arguments.append(modules)
+ if IS_WINDOWS and acceleration == Acceleration.INCREDIBUILD:
+ arg_string = ' '.join(arguments)
+ arguments = [INCREDIBUILD_CONSOLE, f'/command={arg_string}']
+
+ execute(arguments)
+
+ elapsed_time = int(time.time() - start_time)
+ print(f'--- Done({elapsed_time}s) ---')
+
+
+def build_base_docs():
+ arguments = [read_config_python_binary(), "setup.py", "build_base_docs", "--log-level",
+ "quiet"]
+ for build_arg in read_config_build_arguments():
+ if build_arg.startswith("--qt-src-dir="):
+ arguments.append(build_arg)
+ break
+ execute(arguments)
+
+
+def run_tests():
+ """Run tests redirected into a log file with a time stamp"""
+ logfile_name = datetime.datetime.today().strftime("test_%Y%m%d_%H%M.txt")
+ binary = sys.executable
+ command = f'"{binary}" testrunner.py test > {logfile_name}'
+ print(command_log_string([command], Path.cwd()))
+ start_time = time.time()
+ result = 0 if opt_dry_run else os.system(command)
+ elapsed_time = int(time.time() - start_time)
+ print(f'--- Done({elapsed_time}s) ---')
+ return result
+
+
+def create_argument_parser(desc):
+ parser = ArgumentParser(description=desc, formatter_class=RawTextHelpFormatter)
+ parser.add_argument('--dry-run', '-d', action='store_true',
+ help='Dry run, print commands')
+ parser.add_argument('--edit', '-e', action='store_true',
+ help='Edit config file')
+ parser.add_argument('--reset', '-r', action='store_true',
+ help='Git reset hard to upstream state')
+ parser.add_argument('--clean', '-c', action='store_true',
+ help='Git clean')
+ parser.add_argument('--pull', '-p', action='store_true',
+ help='Git pull')
+ parser.add_argument('--build', '-b', action='store_true',
+ help='Build (configure + build)')
+ parser.add_argument('--make', '-m', action='store_true', help='Make')
+ parser.add_argument('--no-install', '-n', action='store_true',
+ help='Run --build only, do not install')
+ parser.add_argument('--Make', '-M', action='store_true',
+ help='cmake + Make (continue broken build)')
+ parser.add_argument('--test', '-t', action='store_true',
+ help='Run tests')
+ parser.add_argument('--Documentation', '-D', action='store_true',
+ help='Run build_base_docs')
+ parser.add_argument('--version', '-v', action='version', version='%(prog)s 1.0')
+ parser.add_argument('--verbose', '-V', action='store_true',
+ help='Turn off --quiet specified in build arguments')
+ parser.add_argument('--unity', '-u', action='store_true',
+ help='Force unity build')
+ parser.add_argument('--no-unity', action='store_true',
+ help='Turn off --unity specified in build arguments')
+ return parser
+
+
+if __name__ == '__main__':
+ git = None
+ base_dir = None
+ config_file = None
+ user = None
+
+ config_file = get_config_file('qfp_tool.conf')
+ argument_parser = create_argument_parser(DESC.replace('%CONFIGFILE%', str(config_file)))
+ options = argument_parser.parse_args()
+ opt_dry_run = options.dry_run
+ opt_verbose = options.verbose
+
+ if options.unity:
+ opt_unity_mode = UnityMode.ENABLE
+ elif options.no_unity:
+ opt_unity_mode = UnityMode.DISABLE
+
+ if options.edit:
+ sys.exit(edit_config_file())
+
+ if options.build:
+ build_mode = BuildMode.BUILD
+ elif options.make:
+ build_mode = BuildMode.MAKE
+ elif options.Make:
+ build_mode = BuildMode.RECONFIGURE
+
+ if build_mode == BuildMode.NONE and not (options.clean or options.reset or options.pull
+ or options.Documentation or options.test):
+ argument_parser.print_help()
+ sys.exit(0)
+
+ git = 'git'
+ if which(git) is None:
+ warnings.warn('Unable to find git', RuntimeWarning)
+ sys.exit(-1)
+
+ if not config_file.exists():
+ print('Create initial config file ', config_file, " ..")
+ with open(config_file, 'w') as f:
+ f.write(DEFAULT_CONFIG_FILE.format(' '.join(DEFAULT_BUILD_ARGS)))
+
+ while not Path(".git").exists():
+ cwd = Path.cwd()
+ cwd_s = os.fspath(cwd)
+ if cwd_s == '/' or (IS_WINDOWS and len(cwd_s) < 4):
+ warnings.warn('Unable to find git root', RuntimeWarning)
+ sys.exit(-1)
+ os.chdir(cwd.parent)
+
+ base_dir = Path.cwd().name
+
+ if options.clean:
+ run_git(['clean', '-dxf'])
+
+ if options.reset:
+ run_git(['reset', '--hard', '@{upstream}'])
+
+ if options.pull:
+ run_git(['pull', '--rebase'])
+
+ if build_mode != BuildMode.NONE:
+ target = 'build' if options.no_install else 'install'
+ build(target)
+
+ if options.Documentation:
+ build_base_docs()
+
+ if options.test:
+ sys.exit(run_tests())
+
+ sys.exit(0)
+++ /dev/null
-# Copyright (C) 2019 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-import datetime
-import os
-import re
-import subprocess
-import sys
-import time
-import warnings
-from argparse import ArgumentParser, RawTextHelpFormatter
-from enum import Enum
-from pathlib import Path
-from typing import List
-
-DESC = """
-Utility script for working with Qt for Python.
-
-Feel free to extend!
-
-Typical Usage:
-Update and build a repository: python qp5_tool -p -b
-
-qp5_tool.py uses a configuration file "%CONFIGFILE%"
-in the format key=value.
-
-It is possible to use repository-specific values by adding a key postfixed by
-a dash and the repository folder base name, eg:
-Modules-pyside-setup512=Core,Gui,Widgets,Network,Test
-
-Configuration keys:
-Acceleration Incredibuild or unset
-BuildArguments Arguments to setup.py
-Generator Generator to be used for CMake. Currently, only Ninja is
- supported.
-Jobs Number of jobs to be run simultaneously
-Modules Comma separated list of modules to be built
- (for --module-subset=)
-Python Python executable (Use python_d for debug builds on Windows)
-
-Arbitrary keys can be defined and referenced by $(name):
-
-MinimalModules=Core,Gui,Widgets,Network,Test
-Modules=$(MinimalModules),Multimedia
-Modules-pyside-setup-minimal=$(MinimalModules)
-"""
-
-
-class Acceleration(Enum):
- NONE = 0
- INCREDIBUILD = 1
-
-
-class BuildMode(Enum):
- NONE = 0
- BUILD = 1
- RECONFIGURE = 2
- MAKE = 3
-
-
-DEFAULT_BUILD_ARGS = ['--build-tests', '--skip-docs', '--quiet']
-IS_WINDOWS = sys.platform == 'win32'
-INCREDIBUILD_CONSOLE = 'BuildConsole' if IS_WINDOWS else '/opt/incredibuild/bin/ib_console'
-# Config file keys
-ACCELERATION_KEY = 'Acceleration'
-BUILDARGUMENTS_KEY = 'BuildArguments'
-GENERATOR_KEY = 'Generator'
-JOBS_KEY = 'Jobs'
-MODULES_KEY = 'Modules'
-PYTHON_KEY = 'Python'
-
-DEFAULT_MODULES = "Core,Gui,Widgets,Network,Test,Qml,Quick,Multimedia,MultimediaWidgets"
-DEFAULT_CONFIG_FILE = f"Modules={DEFAULT_MODULES}\n"
-
-build_mode = BuildMode.NONE
-opt_dry_run = False
-opt_verbose = False
-
-
-def which(needle: str):
- """Perform a path search"""
- needles = [needle]
- if IS_WINDOWS:
- for ext in ("exe", "bat", "cmd"):
- needles.append(f"{needle}.{ext}")
-
- for path in os.environ.get("PATH", "").split(os.pathsep):
- for n in needles:
- binary = Path(path) / n
- if binary.is_file():
- return binary
- return None
-
-
-def command_log_string(args: List[str], directory: Path):
- result = f'[{directory.name}]'
- for arg in args:
- result += f' "{arg}"' if ' ' in arg else f' {arg}'
- return result
-
-
-def execute(args: List[str]):
- """Execute a command and print to log"""
- log_string = command_log_string(args, Path.cwd())
- print(log_string)
- if opt_dry_run:
- return
- exit_code = subprocess.call(args)
- if exit_code != 0:
- raise RuntimeError(f'FAIL({exit_code}): {log_string}')
-
-
-def run_process_output(args):
- """Run a process and return its output. Also run in dry_run mode"""
- std_out = subprocess.Popen(args, universal_newlines=1,
- stdout=subprocess.PIPE).stdout
- result = [line.rstrip() for line in std_out.readlines()]
- std_out.close()
- return result
-
-
-def run_git(args):
- """Run git in the current directory and its submodules"""
- args.insert(0, git) # run in repo
- execute(args) # run for submodules
-
-
-def expand_reference(cache_dict, value):
- """Expand references to other keys in config files $(name) by value."""
- pattern = re.compile(r"\$\([^)]+\)")
- while True:
- match = pattern.match(value)
- if not match:
- break
- key = match.group(0)[2:-1]
- value = value[:match.start(0)] + cache_dict[key] + value[match.end(0):]
- return value
-
-
-def editor():
- editor = os.getenv('EDITOR')
- if not editor:
- return 'notepad' if IS_WINDOWS else 'vi'
- editor = editor.strip()
- if IS_WINDOWS:
- # Windows: git requires quotes in the variable
- if editor.startswith('"') and editor.endswith('"'):
- editor = editor[1:-1]
- editor = editor.replace('/', '\\')
- return editor
-
-
-def edit_config_file():
- exit_code = -1
- try:
- exit_code = subprocess.call([editor(), config_file])
- except Exception as e:
- reason = str(e)
- print(f'Unable to launch: {editor()}: {reason}')
- return exit_code
-
-
-"""
-Config file handling, cache and read function
-"""
-config_dict = {}
-
-
-def read_config_file(file_name):
- """Read the config file into config_dict, expanding continuation lines"""
- global config_dict
- keyPattern = re.compile(r'^\s*([A-Za-z0-9\_\-]+)\s*=\s*(.*)$')
- with open(file_name) as f:
- while True:
- line = f.readline()
- if not line:
- break
- line = line.rstrip()
- match = keyPattern.match(line)
- if match:
- key = match.group(1)
- value = match.group(2)
- while value.endswith('\\'):
- value = value.rstrip('\\')
- value += f.readline().rstrip()
- config_dict[key] = expand_reference(config_dict, value)
-
-
-def read_config(key):
- """
- Read a value from the '$HOME/.qp5_tool' configuration file. When given
- a key 'key' for the repository directory '/foo/qt-5', check for the
- repo-specific value 'key-qt5' and then for the general 'key'.
- """
- if not config_dict:
- read_config_file(config_file)
- repo_value = config_dict.get(f"{key}-{base_dir}")
- return repo_value if repo_value else config_dict.get(key)
-
-
-def read_bool_config(key):
- value = read_config(key)
- return value and value in ['1', 'true', 'True']
-
-
-def read_int_config(key, default=-1):
- value = read_config(key)
- return int(value) if value else default
-
-
-def read_acceleration_config():
- value = read_config(ACCELERATION_KEY)
- if value:
- value = value.lower()
- if value == 'incredibuild':
- return Acceleration.INCREDIBUILD
- return Acceleration.NONE
-
-
-def read_config_build_arguments():
- value = read_config(BUILDARGUMENTS_KEY)
- if value:
- return re.split(r'\s+', value)
- return DEFAULT_BUILD_ARGS
-
-
-def read_config_modules_argument():
- value = read_config(MODULES_KEY)
- if value and value != '' and value != 'all':
- return f"--module-subset={value}"
- return None
-
-
-def read_config_python_binary() -> str:
- binary = read_config(PYTHON_KEY)
- virtual_env = os.environ.get('VIRTUAL_ENV')
- if not binary:
- # Use 'python3' unless virtualenv is set
- use_py3 = not virtual_env and which('python3')
- binary = 'python3' if use_py3 else 'python'
- binary = Path(binary)
- if not binary.is_absolute():
- abs_path = which(str(binary))
- if abs_path:
- binary = abs_path
- else:
- warnings.warn(f'Unable to find "{binary}"', RuntimeWarning)
- if virtual_env:
- if not str(binary).startswith(virtual_env):
- w = f'Python "{binary}" is not under VIRTUAL_ENV "{virtual_env}"'
- warnings.warn(w, RuntimeWarning)
- return str(binary)
-
-
-def get_config_file(base_name) -> Path:
- global user
- home = os.getenv('HOME')
- if IS_WINDOWS:
- # Set a HOME variable on Windows such that scp. etc.
- # feel at home (locating .ssh).
- if not home:
- home = os.getenv('HOMEDRIVE') + os.getenv('HOMEPATH')
- os.environ['HOME'] = home
- user = os.getenv('USERNAME')
- config_file = Path(os.getenv('APPDATA')) / base_name
- else:
- user = os.getenv('USER')
- config_dir = Path(home) / '.config'
- if config_dir.exists():
- config_file = config_dir / base_name
- else:
- config_file = Path(home) / f".{base_name}"
- return config_file
-
-
-def build(target: str):
- """Run configure and build steps"""
- start_time = time.time()
-
- arguments = []
- acceleration = read_acceleration_config()
- if not IS_WINDOWS and acceleration == Acceleration.INCREDIBUILD:
- arguments.append(INCREDIBUILD_CONSOLE)
- arguments.appendh('--avoid') # caching, v0.96.74
- arguments.extend([read_config_python_binary(), 'setup.py', target])
- build_arguments = read_config_build_arguments()
- if opt_verbose and '--quiet' in build_arguments:
- build_arguments.remove('--quiet')
- arguments.extend(build_arguments)
- generator = read_config(GENERATOR_KEY)
- if generator != 'Ninja':
- arguments.extend(['--make-spec', 'ninja'])
- jobs = read_int_config(JOBS_KEY)
- if jobs > 1:
- arguments.extend(['-j', str(jobs)])
- if build_mode != BuildMode.BUILD:
- arguments.extend(['--reuse-build', '--ignore-git'])
- if build_mode != BuildMode.RECONFIGURE:
- arguments.append('--skip-cmake')
- modules = read_config_modules_argument()
- if modules:
- arguments.append(modules)
- if IS_WINDOWS and acceleration == Acceleration.INCREDIBUILD:
- arg_string = ' '.join(arguments)
- arguments = [INCREDIBUILD_CONSOLE, f'/command={arg_string}']
-
- execute(arguments)
-
- elapsed_time = int(time.time() - start_time)
- print(f'--- Done({elapsed_time}s) ---')
-
-
-def run_tests():
- """Run tests redirected into a log file with a time stamp"""
- logfile_name = datetime.datetime.today().strftime("test_%Y%m%d_%H%M.txt")
- binary = sys.executable
- command = f'"{binary}" testrunner.py test > {logfile_name}'
- print(command_log_string([command], Path.cwd()))
- start_time = time.time()
- result = 0 if opt_dry_run else os.system(command)
- elapsed_time = int(time.time() - start_time)
- print(f'--- Done({elapsed_time}s) ---')
- return result
-
-
-def create_argument_parser(desc):
- parser = ArgumentParser(description=desc, formatter_class=RawTextHelpFormatter)
- parser.add_argument('--dry-run', '-d', action='store_true',
- help='Dry run, print commands')
- parser.add_argument('--edit', '-e', action='store_true',
- help='Edit config file')
- parser.add_argument('--reset', '-r', action='store_true',
- help='Git reset hard to upstream state')
- parser.add_argument('--clean', '-c', action='store_true',
- help='Git clean')
- parser.add_argument('--pull', '-p', action='store_true',
- help='Git pull')
- parser.add_argument('--build', '-b', action='store_true',
- help='Build (configure + build)')
- parser.add_argument('--make', '-m', action='store_true', help='Make')
- parser.add_argument('--no-install', '-n', action='store_true',
- help='Run --build only, do not install')
- parser.add_argument('--Make', '-M', action='store_true',
- help='cmake + Make (continue broken build)')
- parser.add_argument('--test', '-t', action='store_true',
- help='Run tests')
- parser.add_argument('--version', '-v', action='version', version='%(prog)s 1.0')
- parser.add_argument('--verbose', '-V', action='store_true',
- help='Turn off --quiet specified in build arguments')
- return parser
-
-
-if __name__ == '__main__':
- git = None
- base_dir = None
- config_file = None
- user = None
-
- config_file = get_config_file('qp5_tool.conf')
- argument_parser = create_argument_parser(DESC.replace('%CONFIGFILE%', str(config_file)))
- options = argument_parser.parse_args()
- opt_dry_run = options.dry_run
- opt_verbose = options.verbose
-
- if options.edit:
- sys.exit(edit_config_file())
-
- if options.build:
- build_mode = BuildMode.BUILD
- elif options.make:
- build_mode = BuildMode.MAKE
- elif options.Make:
- build_mode = BuildMode.RECONFIGURE
-
- if build_mode == BuildMode.NONE and not (options.clean or options.reset
- or options.pull or options.test):
- argument_parser.print_help()
- sys.exit(0)
-
- git = 'git'
- if which(git) is None:
- warnings.warn('Unable to find git', RuntimeWarning)
- sys.exit(-1)
-
- if not config_file.exists():
- print('Create initial config file ', config_file, " ..")
- with open(config_file, 'w') as f:
- f.write(DEFAULT_CONFIG_FILE.format(' '.join(DEFAULT_BUILD_ARGS)))
-
- while not Path(".git").exists():
- cwd = Path.cwd()
- cwd_s = os.fspath(cwd)
- if cwd_s == '/' or (IS_WINDOWS and len(cwd_s) < 4):
- warnings.warn('Unable to find git root', RuntimeWarning)
- sys.exit(-1)
- os.chdir(cwd.parent)
-
- base_dir = Path.cwd().name
-
- if options.clean:
- run_git(['clean', '-dxf'])
-
- if options.reset:
- run_git(['reset', '--hard', '@{upstream}'])
-
- if options.pull:
- run_git(['pull', '--rebase'])
-
- if build_mode != BuildMode.NONE:
- target = 'build' if options.no_install else 'install'
- build(target)
-
- if options.test:
- sys.exit(run_tests())
-
- sys.exit(0)
setup_cmd.append(self.construct_cmd_line_argument(name, value))
# Add --reuse-build option if requested and not already present.
- if (reuse_build and command in ('bdist_wheel', 'build', 'build_rst_docs', 'install')
+ if (reuse_build and command in ('bdist_wheel', 'build', 'build_base_docs', 'build_rst_docs', 'install')
and not self.cmd_line_argument_is_in_args("reuse-build", modified_argv)):
setup_cmd.append(self.construct_cmd_line_argument("reuse-build"))
return setup_cmd
from . import (PYSIDE_PYTHON_TOOLS, PYSIDE_LINUX_BIN_TOOLS, PYSIDE_UNIX_LIBEXEC_TOOLS,
PYSIDE_WINDOWS_BIN_TOOLS, PYSIDE_UNIX_BIN_TOOLS, PYSIDE_UNIX_BUNDLED_TOOLS)
-from setuptools.errors import SetupError
try:
WindowsError
src = Path(src.format(**_vars)) if _vars else Path(src)
if isinstance(dst, str):
dst = Path(dst.format(**_vars)) if _vars else Path(dst)
- assert(isinstance(src, Path))
- assert(isinstance(dst, Path))
+ assert (isinstance(src, Path))
+ assert (isinstance(dst, Path))
if not src.exists() and not force:
log.info(f"**Skipping copy file\n {src} to\n {dst}\n Source does not exist")
src = Path(src.format(**_vars)) if _vars else Path(src)
if isinstance(dst, str):
dst = Path(dst.format(**_vars)) if _vars else Path(dst)
- assert(isinstance(src, Path))
- assert(isinstance(dst, Path))
+ assert (isinstance(src, Path))
+ assert (isinstance(dst, Path))
if _vars is not None:
if _filter is not None:
_path = Path(os.getenv(var, ""))
_pExe = _path / "python.exe"
if not _pExe.is_file():
- log.warn(f"Can't find python.exe from {_pExe}, using default python3")
+ log.warning(f"Can't find python.exe from {_pExe}, using default python3")
_pExe = Path(os.getenv("PYTHON3_32_PATH")) / "python.exe"
else:
_pExe = Path(os.getenv("PYTHON2_32_PATH")) / "python.exe"
_path = Path(os.getenv(var, ""))
_pExe = _path / "python.exe"
if not _pExe.is_file():
- log.warn(f"Can't find python.exe from {_pExe}, using default python3")
+ log.warning(f"Can't find python.exe from {_pExe}, using default python3")
_pExe = Path(os.getenv("PYTHON3_PATH")) / "python.exe"
env_python = f"{_env}\\Scripts\\python.exe"
env_pip = f"{_env}\\Scripts\\pip.exe"
except Exception as e:
print(f"Exception {type(e).__name__}: {e}")
_pExe = "python3"
- return(_pExe, _env, env_pip, env_python)
+ return (_pExe, _env, env_pip, env_python)
def run_instruction(instruction, error, initial_env=None):
if tool_exist(bin_path / f"{tool}.exe")])
else:
lib_exec_path = qt_tools_path / "Qt" / "libexec" if package_for_wheels \
- else qt_tools_path / "libexec"
+ else qt_tools_path / "libexec"
pyside_tools.extend([tool for tool in PYSIDE_UNIX_LIBEXEC_TOOLS
if tool_exist(lib_exec_path / tool)])
if sys.platform == 'darwin':
module_QtQml(),
module_QtQuick(),
module_QtQuickControls2(),
+ module_QtQuickTest(),
module_QtQuickWidgets(),
module_QtXml(),
module_QtTest(),
"libQt6QmlCore",
"libQt6QmlLocalStorage",
"libQt6QmlModels",
+ "libQt6QmlNetwork",
"libQt6QmlWorkerScript",
"libQt6QmlXmlListModel",
"libQt6QmlCompiler"
"libQt6QuickTemplates2",
"libQt6QuickTest",
"libQt6QuickTimeline",
+ "libQt6QuickTimelineBlendTrees",
]
# Adding GraphicalEffects files
def module_QtQuickControls2() -> ModuleData:
data = ModuleData("QuickControls2")
+ data.qtlib.append("libQt6QuickControls2")
+ data.qtlib.append("libQt6QuickControls2Basic")
+ data.qtlib.append("libQt6QuickControls2BasicStyleImpl")
+ data.qtlib.append("libQt6QuickControls2Fusion")
+ data.qtlib.append("libQt6QuickControls2FusionStyleImpl")
+ data.qtlib.append("libQt6QuickControls2Imagine")
+ data.qtlib.append("libQt6QuickControls2ImagineStyleImpl")
data.qtlib.append("libQt6QuickControls2Impl")
+ data.qtlib.append("libQt6QuickControls2Material")
+ data.qtlib.append("libQt6QuickControls2MaterialStyleImpl")
+ data.qtlib.append("libQt6QuickControls2Universal")
+ data.qtlib.append("libQt6QuickControls2UniversalStyleImpl")
+ if sys.platform == "win32":
+ data.qtlib.append("libQt6QuickControls2WindowsStyleImpl")
+ elif sys.platform == "darwin":
+ data.qtlib.append("libQt6QuickControls2IOSStyleImpl")
+ data.qtlib.append("libQt6QuickControls2MacOSStyleImpl")
+
data.metatypes.append("qt6quickcontrols2impl_relwithdebinfo_metatypes.json")
return data
+def module_QtQuickTest() -> ModuleData:
+ data = ModuleData("QuickTest")
+
+ return data
+
+
def module_QtQuickWidgets() -> ModuleData:
data = ModuleData("QuickWidgets")
return data
"libQt6Quick3DEffects",
"libQt6Quick3DGlslParser",
"libQt6Quick3DHelpers",
+ "libQt6Quick3DHelpersImpl",
"libQt6Quick3DIblBaker",
"libQt6Quick3DParticleEffects",
"libQt6Quick3DParticles",
+ "libQt6Quick3DPhysics",
+ "libQt6Quick3DPhysicsHelpers",
"libQt6Quick3DRuntimeRender",
+ "libQt6Quick3DSpatialAudio",
"libQt6Quick3DUtils",
"libQt6ShaderTools",
"libQt63DQuick",
data.qtlib.extend(_qtlib)
data.metatypes.extend(_metatypes)
data.extra_files.append("Qt/plugins/assetimporters/libassimp*")
+ data.extra_files.append("qsb*")
+ data.extra_files.append("balsam*")
return data
data.translations.append("qtmultimedia_*")
data.plugins = get_module_plugins(json_data)
+ if sys.platform == "win32":
+ data.extra_files.extend(["avcodec-60.dll", "avformat-60.dll", "avutil-58.dll",
+ "swresample-4.dll", "swscale-7.dll"])
+
return data
def module_QtVirtualKeyboard() -> ModuleData:
data = ModuleData("VirtualKeyboard")
data.plugins.append("virtualkeyboard")
+ data.qtlib.append("libQt6VirtualKeyboardSettings")
+
return data
product_dependency:
../../qt/qt5:
- ref: "15b7e7434fd79334a5cf071e36cee7663fe1fb45"
+ ref: "3f005f1e2e88485dbf541200ba3fafcad6ea84ad"
dependency_source: supermodule
dependencies: [
"../../qt/qt3d",
- condition: property
property: host.os
equals_value: Windows
+ - type: EnvironmentVariable
+ variableName: PYTHON3_PATH
+ variableValue: "{{ index .Env \"PYTHON3.10.0-64_PATH\"}}"
+ enable_if:
+ condition: and
+ conditions:
+ - condition: property
+ property: host.osVersion
+ equals_value: Windows_11_22H2
+ - condition: property
+ property: host.os
+ equals_value: Windows
- type: EnvironmentVariable
variableName: TARGET_ARCHITECTURE
variableValue: amd64_x86
equals_value: MacOS
- type: PrependToEnvironmentVariable
variableName: PATH
- variableValue: "{{.Env.PYTHON3_PATH}};"
+ variableValue: "{{ index .Env \"PYTHON3.10.0-64_PATH\"}};"
enable_if:
condition: property
property: host.os
- condition: property
property: host.os
equals_value: MacOS
- - type: ExecuteCommand
- command: "keyring --disable"
- maxTimeInSeconds: 14400
- maxTimeBetweenOutput: 1200
- enable_if:
- condition: and
- conditions:
- - condition: property
- property: host.os
- equals_value: Linux
- - condition: property
- property: host.arch
- equals_value: AARCH64
- userMessageOnFailure: >
- Failed to disable keyring
- type: ExecuteCommand
command: "sudo apt-get install python3-pip libclang-11-dev clang -y"
maxTimeInSeconds: 14400
userMessageOnFailure: >
Failed to install dependencies
- type: ExecuteCommand
- command: "python3 -m pip install -U setuptools==67.7.2"
+ command: "python3 -m pip install -U setuptools==69.1.1"
maxTimeInSeconds: 14400
maxTimeBetweenOutput: 1200
enable_if:
type: Group
instructions:
- type: ExecuteCommand
- command: "python3 -m pip install -U setuptools==67.7.2"
+ command: "python3 -m pip install -U setuptools==69.1.1"
maxTimeInSeconds: 14400
maxTimeBetweenOutput: 1200
enable_if:
equals_value: MacOS
userMessageOnFailure: >
Failed to execute build instructions on macOS
- - type: EnvironmentVariable
- variableName: PYSIDE_SIGNING_DIR
- variableValue: "{{.AgentWorkingDir}}\\pyside\\{{.Env.TESTED_MODULE_COIN}}\\build\\qfpa-p3.8\\package_for_wheels"
- enable_if:
- condition: property
- property: host.osVersion
- contains_value: "Windows_11"
- type: EnvironmentVariable
variableName: PYSIDE_SIGNING_DIR
variableValue: "{{.AgentWorkingDir}}\\pyside\\{{.Env.TESTED_MODULE_COIN}}\\build\\qfpa-p3.10\\package_for_wheels"
enable_if:
condition: property
- property: host.osVersion
- contains_value: "Windows_10"
+ property: host.os
+ equals_value: Windows
- type: ExecuteCommand
- command: "{{.Env.interpreter}} -m pip install -U pip setuptools==67.7.2 --user"
+ command: "{{.Env.interpreter}} -m pip install -U pip setuptools==69.1.1 --user"
maxTimeInSeconds: 14400
maxTimeBetweenOutput: 1200
enable_if:
userMessageOnFailure: >
Failed to execute build instructions on Linux
- type: ExecuteCommand
- command: "c:\\users\\qt\\MSVC.bat {{.Env.PYTHON3_PATH}}\\python.exe -m pip install -U setuptools==67.7.2"
+ command: "c:\\users\\qt\\MSVC.bat {{.Env.PYTHON3_PATH}}\\python.exe -m pip install -U setuptools==69.1.1"
maxTimeInSeconds: 14400
maxTimeBetweenOutput: 1200
enable_if:
not_contains_value: LicenseCheck
instructions:
- type: ExecuteCommand
- command: "python3 -m pip install -U setuptools==67.7.2"
+ command: "python3 -m pip install -U setuptools==69.1.1"
maxTimeInSeconds: 14400
maxTimeBetweenOutput: 1200
enable_if:
userMessageOnFailure: >
Failed to execute test instructions on macOS
- type: ExecuteCommand
- command: "{{.Env.interpreter}} -m pip install -U pip setuptools==67.7.2 --user"
+ command: "{{.Env.interpreter}} -m pip install -U pip setuptools==69.1.1 --user"
maxTimeInSeconds: 14400
maxTimeBetweenOutput: 1200
enable_if:
userMessageOnFailure: >
Failed to execute test instructions on Linux
- type: ExecuteCommand
- command: "c:\\users\\qt\\MSVC.bat {{.Env.PYTHON3_PATH}}\\python.exe -m pip install -U pip setuptools==67.7.2 --user"
+ command: "c:\\users\\qt\\MSVC.bat {{.Env.PYTHON3_PATH}}\\python.exe -m pip install -U pip setuptools==69.1.1 --user"
maxTimeInSeconds: 14400
maxTimeBetweenOutput: 1200
enable_if:
condition: property
property: host.compiler
equals_value: MSVC2019
+ - type: EnvironmentVariable
+ variableName: VC_SCRIPT
+ variableValue: "\\Program Files\\Microsoft Visual Studio\\2022\\Professional\\VC\\Auxiliary\\Build\\vcvarsall.bat"
+ enable_if:
+ condition: property
+ property: host.compiler
+ equals_value: MSVC2022
- type: WriteFile
fileContents: "call \"{{.Env.VC_SCRIPT}}\" {{.Env.TARGET_ARCHITECTURE}} \r\ncmd /c %*"
filename: "c:\\users\\qt\\MSVC.bat"
not_contains_value: UseLegacyInstructions
- condition: property # MinGW and msvc2015 are not supported
property: target.compiler
- not_in_values: [Mingw, MSVC2015]
+ not_in_values: [Mingw, MSVC2015,Clang]
- condition: property # Webassembly
property: target.osVersion
not_equals_value: WebAssembly
- condition: property # Windows on Arm
property: target.arch
not_equals_value: ARM64
+ - condition: property # Windows on Arm host build
+ property: target.arch
+ not_equals_value: AARCH64
- condition: property
property: features
not_contains_value: DebianPackaging
+ - condition: property
+ property: target.osVersion
+ not_equals_value: VxWorks
- condition: and
conditions:
- - condition: property # No cross compilation
- property: host.osVersion
- equals_value: MacOS_10_15
+ - condition: property
+ property: host.os
+ equals_value: MacOS
- condition: property
property: features
contains_value: TestOnly
- condition: property
property: features
contains_value: Packaging
+ - condition: property
+ property: target.os
+ not_contains_value: Android
+ - condition: property
+ property: target.os
+ not_contains_value: IOS
- condition: and
conditions:
- condition: property
property: host.osVersion
equals_value: MacOS_11_00
+ - condition: property
+ property: host.arch
+ equals_value: ARM64
+ - condition: property
+ property: features
+ contains_value: TestOnly
- condition: property
property: features
contains_value: Packaging
- - condition: and
+ - condition: and # Restore LoA config
conditions:
- condition: property
property: host.osVersion
- equals_value: MacOS_11_00
+ equals_value: Debian_11_6
- condition: property
property: host.arch
- equals_value: ARM64
+ equals_value: AARCH64
- condition: property
property: features
- contains_value: TestOnly
+ not_contains_value: DebianPackaging
- condition: property
property: features
contains_value: Packaging
+
machine_type:
Build:
cores: 8
python_ver = "3.11"
wheel_package_dir = "qfpa-p3.6"
if CI_TARGET_OS in ["Windows"]:
- if (os.environ.get("HOST_OSVERSION_COIN")).startswith("windows_10"):
- python_ver = "3.10.0"
- else:
- python_ver = "3.8.1"
+ python_ver = "3.10.0"
if CI_TEST_PHASE in ["ALL", "BUILD"]:
call_setup(python_ver, "BUILD")
# Until CI has a feature to set more dynamic signing dir, make sure it actually exist
# In win machines, there are additional python versions to test with
if CI_HOST_OS == "Windows":
- if (os.environ.get('HOST_OSVERSION_COIN')).startswith('windows_10'):
- call_testrunner("3.10.0", str(testRun))
- else:
- call_testrunner("3.8.1", str(testRun))
+ call_testrunner("3.10.0", str(testRun))
elif CI_HOST_OS == "Linux":
call_testrunner("3.11", str(testRun))
else:
import platform
import sys
import importlib
+import json
from argparse import ArgumentParser, Namespace
from dataclasses import dataclass
from pathlib import Path
return version, f"{name}.__init__.__version__"
-def get_manifest(wheel_name: str, data: List[ModuleData]) -> str:
+def create_module_plugin_json(wheel_name: str, data: List[ModuleData], package_path: Path):
+ all_plugins = {}
+
+ for module in data:
+ all_plugins[module.name] = getattr(module, "plugins")
+
+ # write the dictionary modules->plugins dictionary to a .json file and include this .json file
+ # This file is picked up by the deployment tool to figure out the plugin dependencies
+ # of a PySide6 application
+ if all_plugins:
+ with open(f"{package_path}/PySide6/{wheel_name}.json", 'w') as fp:
+ json.dump(all_plugins, fp, indent=4)
+
+
+def get_manifest(wheel_name: str, data: List[ModuleData], package_path: Path) -> str:
lines = []
for module in data:
lines.append("recursive-exclude PySide6/Qt/qml *.debug")
lines.append("prune PySide6/Qt/qml/QtQuick3D/MaterialEditor")
+ # adding PySide6_Essentials.json and PySide6_Addons.json
+ lines.append(f"include PySide6/{wheel_name}.json")
+
return "\n".join(lines)
with open(pyproject_toml_path, "w") as f:
f.write(pyproject_toml_content)
- # 3. Create the 'MANIFEST.in'
+ # 3. Create PySide_Essentials.json and PySide_Addons.json
+ # creates a json file mapping each Qt module to the possible plugin dependencies
+ if data is not None:
+ print(f"-- Creating {name}.json")
+ create_module_plugin_json(name, data, package_path)
+
+ # 4. Create the 'MANIFEST.in'
# Special case for shiboken and shiboken_generator
# so we copy the whole directory, only PySide and derivatives
# will need to have specific information
if data is None:
manifest_content = get_simple_manifest(name)
else:
- manifest_content = get_manifest(name, data)
+ manifest_content = get_manifest(name, data, package_path)
with open(package_path / "MANIFEST.in", "w") as f:
f.write(manifest_content)
for fname in _files:
copy(fname, package_path)
- # 5. call the build module to create the wheel
+ # 6. call the build module to create the wheel
print("-- Creating wheels")
if not verbose:
_runner = pyproject_hooks.quiet_subprocess_runner
builder = build.ProjectBuilder(package_path, runner=_runner)
builder.build("wheel", "dist")
- # 6. Copy wheels back
+ # 7. Copy wheels back
print("-- Copying wheels to dist/")
dist_path = Path("dist")
if not dist_path.is_dir():
--- /dev/null
+Qt for Python 6.6.3 is a bug-fix release.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qtforpython/
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+* PySide6 *
+****************************************************************************
+
+ - pyside6-project now also builds translation (.qm) files.
+ - [PYSIDE-769] QtAsyncio: Fix a missing attribute error on Windows. Fix
+ - [PYSIDE-2641] QtAsyncio.run() not returning the result of a coroutine. It
+ is now possible to disable the signal handler for SIGINT by
+ passing a parameter to run(). The loop policy is reset after
+ run() finishes.
+ - [PYSIDE-1112] Documentation: The list of tools has been updated.
+ - [PYSIDE-1112] Documentation: A page on the pyside6-project tool
+ has been added.
+ - [PYSIDE-1955] Type hints: Add support for classmethods
+ - [PYSIDE-2206] An exit crash of the audiooutput example has been fixed.
+ - [PYSIDE-2263] Type hints: Support for class attributes has been added.
+ - [PYSIDE-2468] Documentation on the PySide6 Permission API has been added.
+ - [PYSIDE-2590] Some fixes have been made be able to cope with namespaced Qt
+ builds.
+ - [PYSIDE-2599] The conversion of QVariant<->QMatrix<n>x<m> has been fixed.
+ - [PYSIDE-2603] Documentation: A broken link to the widget styling tutorial
+ has been fixed.
+ - [PYSIDE-2610] Inheritance from QAbstractTextureImage/
+ QTextureImageDataGenerator has been fixed.
+ - [PYSIDE-2612] Plain CMake builds with Limited API on Windows have been
+ fixed.
+ - [PYSIDE-2613] Connecting to signals with QFlags<> arguments has been fixed.
+ - [PYSIDE-2627] A regression breaking calling Signal.connect() with
+ QObject-derived callables has been fixed.
+ - [PYSIDE-2628] A crash using struct.unpack() on a QByteArray with
+ Limited API has been fixed.
+ - [PYSIDE-2638] A crash when adding None to a QLayout has been fixed.
+ - [PYSIDE-2639] macOS: A segmentation fault in QLocale.system() has been
+ fixed.
+ - [PYSIDE-2640] pyside6-qml now uses a QQuickView when the rootobject is a
+ QQuickItem.
+
+****************************************************************************
+* Shiboken6 *
+****************************************************************************
+
+ - [PYSIDE-2505] Command line option parsing has been improved to skip empty
+ tokens when in include paths.
+ - [PYSIDE-2619] An ODR violation in shibobken code has been fixed, enabling
+ LTO.
--- /dev/null
+Qt for Python 6.7.0 is a minor release.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qtforpython/
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+* PySide6 *
+****************************************************************************
+
+ - setup.py now uses CMake Unity Build Mode by default.
+ - The namespace QAudio has been renamed to QtAudio in Qt 6.7. An alias
+ has been created, but it may not work in all cases.
+ - pyside6-project now has an lupdate mode updating translation files (.ts)
+ from the sources.
+ - Python code has been updated to be free of flake8 warnings using
+ a custom configuration.
+ - For deployment, the supported version of Nuitka has been raised to 2.1.0.
+ - singleShot timers with a timeout of 0 now call the C++ function (which
+ directly calls QMetaObject::invokeMethod) instead of manually creating and
+ starting a timer object, slightly improving performance.
+ - [PYSIDE-769] QtAsyncio: Fix a missing attribute error on Windows. Fix
+ - [PYSIDE-2641] QtAsyncio.run() not returning the result of a coroutine. It
+ is now possible to disable the signal handler for SIGINT by
+ passing a parameter to run(). The loop policy is reset after
+ run() finishes.
+ - [PYSIDE-838] Functions qCompress/qUncompress() taking a PyBuffer/len
+ arguments have been fixed.
+ - [PYSIDE-1106] Documentation: Decorators, global functions and enumerations
+ are now documented add indexed from the module page. The class
+ documentation now uses more sphinx domain directives.
+ Pages for all tools have been added. The order of the examples
+ list has been changed to show relevant examples first.
+ - [PYSIDE-1275] QObject.disconnect() now returns False with a warning instead
+ of raising an exception for non-fatal cases.
+ - [PYSIDE-1612] Deployment: Issues when using pyenv have been fixed. The
+ finding of dependent modules has been improved. On Windows,
+ this now requires the dumpbin tool, which is added to the path
+ by calling the vcvarsall.bat script of Microsoft Visual
+ Studio.
+ - [PYSIDE-1612] Android Deployment: The NDK version in the tool has been
+ updated. Python has been updated to 3.11. CMake version 3.23
+ is now required. The wheels are no longer built with
+ --limited-api. The generated pysidedeploy.spec has an explicit
+ group called `android` which stores the wheels and plugins.
+ - [PYSIDE-1906] Hash value calculation has been optimized.
+ - [PYSIDE-2206] The colorpaletteclient demo as well as the hellographs and
+ simplerhiwidget examples have been added.
+ - [PYSIDE-2215] Intersphinx support has been improved.
+ - [PYSIDE-2280] A type for os.PathLike type hints has been added.
+ - [PYSIDE-2404] Application startup time has been significantly decreased
+ by creating the types on demand. The importing of numpy
+ has also been deferred since it caused quite some delay.
+ - [PYSIDE-2432] A static create() function for decorated QML singletons
+ has been added.
+ - [PYSIDE-2484] The QML type registration code has been ported to use
+ RegisterTypeAndRevisions.
+ - [PYSIDE-2535] Obtaining DBUS properties from QDBusInterface has been fixed.
+ As a consequence though, it is no longer possible to derive a
+ Python class with signals/slots from QDBusInterface.
+ - [PYSIDE-2504] Documentation: Option 'build_rst_docs' has been deprecated in
+ favor of 'build_base_docs'.
+ - [PYSIDE-2524] It is now possible to connect signals to slots/lambdas with
+ more arguments provided they have default parameters.
+ - [PYSIDE-2524] The signal connection code has been optimized.
+ - [PYSIDE-2543] QtQuickTest has been added.
+ - [PYSIDE-2576] Documentation: The extendedexplorer tutorial has been improved.
+ - [PYSIDE-2590] Some fixes have been made be able to cope with namespaced Qt
+ builds.
+ - [PYSIDE-2610] The QTextureImageData methods returning an enumeration of
+ QOpenGLTexture have been added.
+ - [PYSIDE-2605] An error about the C++ object being deleted when calling
+ QWidget.style() in an embedded application has been fixed.
+ - [PYSIDE-2468] Deployment: Support for the Qt permission API has been added.
+ macOS bundle applications are now created.
+ - [PYSIDE-2597] Desktop Deployment: The the plugins included have been
+ optimized.
+ - [PYSIDE-2633] C++ typedefs of container instantiations are now
+ registered in Python under their name, making possible
+ to decorate slots with those names (for example,
+ "QRemoteObjectSourceLocation").
+ - [PYSIDE-2639] Segmentation fault with QLocale.system() has been fixed.
+ - [PYSIDE-2652] A bug when passing values of unsigned long long type
+ exceeding the long long maximum value to float
+ parameters has been fixed.
+ - [PYSIDE-2663] A crash in QtWebEngine browsing https://outlook.com has been
+ fixed.
+ - [PYSIDE-2668] A bug when comparing QOperatingSystemVersion::OSType
+ has been fixed.
+ - [QTBUG-119785] The Filesystemexplorer-example has been updated.
+
+****************************************************************************
+* Shiboken6 *
+****************************************************************************
+
+ - [PYSIDE-31] Shiboken.wrapInstance() now returns existing instances
+ (preserving ids).
+ - [PYSIDE-560] libshiboken/Limited API: No longer needed PyTypeObject slots
+ have been disabled in the PyTypeObject helper struct.
+ - [PYSIDE-1106] A typesystem attribute providing a hint to a documentation
+ file has been added to function/enum type entries
+ (for globals).
+ - [PYSIDE-1106] Documentation can now be injected from separate .rst files.
+ - [PYSIDE-1106] Documentation injected into classes with "append" will now
+ be appended to the class description instead of being written
+ at the end of the page.
+ - [PYSIDE-1106] Documentation: It is now possible to inject documentation
+ for parameters of added functions.
+ - [PYSIDE-2230] Python 3.12: Hidden Type Extensions according to PEP 697
+ are now used instead of shadow dictionaries.
+ - [PYSIDE-2404] The generated type index constants are no longer in
+ uppercase. Uppercase is retained until deprecation in
+ PySide7.
+ - [PYSIDE-2447] A typesystem attribute to generate submodules has been added.
+ - [PYSIDE-2535] Generating the Qt meta object functions handling the PySide6
+ signals can now be disabled by a typesystem attribute. This
+ is useful for classes using dynamic meta objects, for
+ example QDBusInterface.
--- /dev/null
+Qt for Python 6.7.1 is a bug-fix release.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qtforpython/
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+* PySide6 *
+****************************************************************************
+
+ - [PYSIDE-487] A number of missing classes have been added.
+ - [PYSIDE-2629] Tooling: pyside6-qsb, pyside6-balsam and pyside6-balsamui
+ have been added.
+ - [PYSIDE-2644] QtAsyncio: An issue with tasks with loop not cancelling
+ has been fixed.
+ - [PYSIDE-2663] A crash when browsing https://outlook.com has been fixed.
+ - [PYSIDE-2665] A syntax error in the pyi-files has been fixed.
+ - [PYSIDE-2668] The comparison of QOperatingSystemVersion.OSType has been
+ fixed.
+ - [PYSIDE-2675] Lazy Load: An issue with polymorphic classes has been
+ fixed.
+ - [PYSIDE-2676] A crash with Python 3.12 when creating classes from
+ meta classes has been fixed.
+ - [PYSIDE-2685] An error in the pyi-files related to the import of
+ NoneType has been fixed.
+ - [PYSIDE-2686] Missing imports for types of return values
+ have been added to the pyi-files.
+ - [PYSIDE-2698] A crash when querying the size of QtQml.ListProperty
+ has been fixed and documentation for QtQml.ListProperty
+ has been added.
+ - [PYSIDE-2705] Warnings about failures of QObject.disconnect() can
+ now be suppressed.
+ - [PYSIDE-2709] A bug using legacy qmlRegisterType() for class hierarchies
+ has been fixed.
+ - [QTBUG-123997] Multimedia: The renaming of the namespace QAudio to
+ QtAudio has been undone following a revert in Qt.
+
+****************************************************************************
+* Shiboken6 *
+****************************************************************************
+
+ - [PYSIDE-2590] An attribute for global inline namespace scopes has been
+ added.
+ - [PYSIDE-2602] Generate Python override code for added virtuals
+ - [PYSIDE-2602] Support for virtual functions with return type
+ modifications has been added and the function
+ QWebEnginePage.javaScriptPrompt()
+ has been fixed accordingly.
+ - [PYSIDE-2675] A code snippet placeholder for the base class for
+ polymorphic-id-expressions has been added, fixing
+ a potentially undefined behavior when using the
+ derived classes.
--- /dev/null
+Qt for Python 6.7.2 is a bug-fix release.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qtforpython/
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+* PySide6 *
+****************************************************************************
+
+ - [PYSIDE-1612] Android Deployment now used the development branch of p4a.
+ - [PYSIDE-2712] Type hints: Modified types are no longer considered
+ as eligible implicit conversions.
+ - [PYSIDE-2745] Exceptions occurring in a slot connected to a
+ 0-delay singleShot timer signal are no longer suppressed.
+ - [PYSIDE-2747] Running on Fedora 40 with Python 3.12.3-2 has been fixed.
+ - [PYSIDE-2748] Type hints: QTranslator.translate() has been fixed.
+ - [PYSIDE-2749] Lazy Load: A performance regression showing in Qt event
+ filters has been fixed.
+ - [PYSIDE-2750] Qt6VirtualKeyboardSettings have been added to the wheel.
+ - [PYSIDE-2756] Type hints: The return type of QItemSelection.__init__()
+ has been fixed.
+ - [PYSIDE-2758] QQuickWebEngineProfile.setUrlRequestInterceptor()
+ has been added.
+ - [PYSIDE-2759] The ownership of cache object passed to
+ QNetworkAccessManager.setCache() has been fixed.
+ - [PYSIDE-2762] Type hints: The return type of
+ QModelIndex.internalPointer() has been fixed.
+ - [PYSIDE-2767] Type hints: An error checking the property decorator
+ has been fixed.
+ - [PYSIDE-2768] Type hints: smart pointer signatures have been fixed.
+
+****************************************************************************
+* Shiboken6 *
+****************************************************************************
+
+ - [PYSIDE-2764] The missing declaration of the deprecated variable
+ cppApiVariableNameOld has been added to the generated
+ headers.
+ - [PYSIDE-2769] Finding the clang include directories on
+ manylinux_2_28_x86_64 has been fixed.
main_window.show()
- QtAsyncio.run(eratosthenes.start())
+ QtAsyncio.run(eratosthenes.start(), handle_sigint=True)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-from PySide6.QtCore import (Qt, QObject, Signal, Slot)
+from PySide6.QtCore import Qt
from PySide6.QtWidgets import (QApplication, QLabel, QMainWindow, QPushButton, QVBoxLayout, QWidget)
import PySide6.QtAsyncio as QtAsyncio
class MainWindow(QMainWindow):
- start_signal = Signal()
-
def __init__(self):
super().__init__()
layout.addWidget(self.text, alignment=Qt.AlignmentFlag.AlignCenter)
async_trigger = QPushButton(text="What is the question?")
- async_trigger.clicked.connect(self.async_start)
+ async_trigger.clicked.connect(lambda: asyncio.ensure_future(self.set_text()))
layout.addWidget(async_trigger, alignment=Qt.AlignmentFlag.AlignCenter)
- @Slot()
- def async_start(self):
- self.start_signal.emit()
-
async def set_text(self):
await asyncio.sleep(1)
self.text.setText("What do you get if you multiply six by nine?")
-class AsyncHelper(QObject):
-
- def __init__(self, worker, entry):
- super().__init__()
- self.entry = entry
- self.worker = worker
- if hasattr(self.worker, "start_signal") and isinstance(self.worker.start_signal, Signal):
- self.worker.start_signal.connect(self.on_worker_started)
-
- @Slot()
- def on_worker_started(self):
- asyncio.ensure_future(self.entry())
-
-
if __name__ == "__main__":
app = QApplication(sys.argv)
main_window = MainWindow()
- async_helper = AsyncHelper(main_window, main_window.set_text)
-
main_window.show()
- QtAsyncio.run()
+ QtAsyncio.run(handle_sigint=True)
################################################################################
## Form generated from reading UI file 'device.ui'
##
-## Created by: Qt User Interface Compiler version 6.2.3
+## Created by: Qt User Interface Compiler version 6.7.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
################################################################################
## Form generated from reading UI file 'service.ui'
##
-## Created by: Qt User Interface Compiler version 6.2.3
+## Created by: Qt User Interface Compiler version 6.7.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide6.QtQml import QmlElement
from PySide6.QtCore import QObject, Property, Signal, Slot, Qt
-from heartrate_global import simulator, is_android
+from heartrate_global import simulator, is_android, error_not_nuitka
-if is_android:
+if is_android or sys.platform == "darwin":
from PySide6.QtCore import QBluetoothPermission
# To be used on the @QmlElement decorator
self.deviceChanged.emit()
def initLocalDevice(self):
- if is_android:
+ if is_android or sys.platform == "darwin":
+ error_not_nuitka()
permission = QBluetoothPermission()
permission.setCommunicationModes(QBluetoothPermission.Access)
permission_status = qApp.checkPermission(permission) # noqa: F821
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+import sys
from PySide6.QtBluetooth import (QBluetoothDeviceDiscoveryAgent,
QBluetoothDeviceInfo)
from bluetoothbaseclass import BluetoothBaseClass
from deviceinfo import DeviceInfo
-from heartrate_global import simulator, is_android
+from heartrate_global import simulator, is_android, error_not_nuitka
-if is_android:
+if is_android or sys.platform == "darwin":
from PySide6.QtCore import QBluetoothPermission
# To be used on the @QmlElement decorator
@Slot()
def startSearch(self):
- if is_android:
+ if is_android or sys.platform == "darwin":
+ error_not_nuitka()
permission = QBluetoothPermission()
permission.setCommunicationModes(QBluetoothPermission.Access)
permission_status = qApp.checkPermission(permission) # noqa: F821
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import os
-
+import sys
_simulator = False
is_android = os.environ.get('ANDROID_ARGUMENT')
+
+
+def error_not_nuitka():
+ """Errors and exits for macOS if run in interpreted mode.
+ """
+ is_nuitka = "__compiled__" in globals()
+ if not is_nuitka and sys.platform == "darwin":
+ print("This example does not work on macOS when Python is run in interpreted mode."
+ "For this example to work on macOS, package the example using pyside6-deploy"
+ "For more information, read `Notes for Developer` in the documentation")
+ sys.exit(0)
################################################################################
## Form generated from reading UI file 'themewidget.ui'
##
-## Created by: Qt User Interface Compiler version 6.2.3
+## Created by: Qt User Interface Compiler version 6.7.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self.horizontalLayout.addWidget(self.antialiasCheckBox)
- self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
+ self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
self.horizontalLayout.addItem(self.horizontalSpacer)
################################################################################
## Form generated from reading UI file 'dialog.ui'
##
-## Created by: Qt User Interface Compiler version 6.2.3
+## Created by: Qt User Interface Compiler version 6.7.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
--- /dev/null
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+import QtExampleStyle
+
+Popup {
+ id: colorDeleter
+ padding: 10
+ modal: true
+ focus: true
+ anchors.centerIn: parent
+ closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
+ signal deleteClicked(int cid)
+
+ property int colorId: -1
+
+ property string colorName: ""
+
+ function maybeDelete(color_id, name) {
+ colorName = name
+ colorId = color_id
+ open()
+ }
+
+
+ ColumnLayout {
+ anchors.fill: parent
+ spacing: 10
+
+ Text {
+ color: "#222222"
+ text: qsTr("Delete Color?")
+ font.pixelSize: 16
+ font.bold: true
+ }
+
+ Text {
+ color: "#222222"
+ text: qsTr("Are you sure, you want to delete color") + " \"" + colorDeleter.colorName + "\"?"
+ font.pixelSize: 12
+ }
+
+ RowLayout {
+ Layout.fillWidth: true
+ spacing: 10
+
+ Button {
+ Layout.fillWidth: true
+ text: qsTr("Cancel")
+ onClicked: colorDeleter.close()
+ }
+
+ Button {
+ Layout.fillWidth: true
+ text: qsTr("Delete")
+
+ buttonColor: "#CC1414"
+ textColor: "#FFFFFF"
+
+ onClicked: {
+ colorDeleter.deleteClicked(colorDeleter.colorId)
+ colorDeleter.close()
+ }
+ }
+ }
+ }
+}
--- /dev/null
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQuick.Dialogs
+
+import QtExampleStyle
+
+Popup {
+ id: colorEditor
+ // Popup for adding or updating a color
+ padding: 10
+ modal: true
+ focus: true
+ anchors.centerIn: parent
+ closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
+ signal colorAdded(string name, string color, string pantone_value)
+ signal colorUpdated(string name, string color, string pantone_value, int cid)
+
+ property bool newColor: true
+ property int colorId: -1
+ property alias currentColor: colordialogButton.buttonColor
+
+ function createNewColor() {
+ newColor = true
+ colorNameField.text = "cute green"
+ colorRGBField.text = "#41cd52"
+ colorPantoneField.text = "PMS 802C"
+ open()
+ }
+
+ function updateColor(color_id, name, color, pantone_value) {
+ newColor = false
+ colorNameField.text = name
+ currentColor = color
+ colorPantoneField.text = pantone_value
+ colorId = color_id
+ open()
+ }
+
+ ColorDialog {
+ id: colorDialog
+ title: qsTr("Choose a color")
+ onAccepted: {
+ colorEditor.currentColor = Qt.color(colorDialog.selectedColor)
+ colorDialog.close()
+ }
+ onRejected: {
+ colorDialog.close()
+ }
+ }
+
+ ColumnLayout {
+ anchors.fill: parent
+ spacing: 10
+
+ GridLayout {
+ columns: 2
+ rowSpacing: 10
+ columnSpacing: 10
+
+ Label {
+ text: qsTr("Color Name")
+ }
+ TextField {
+ id: colorNameField
+ padding: 10
+ }
+
+ Label {
+ text: qsTr("Pantone Value")
+ }
+ TextField {
+ id: colorPantoneField
+ padding: 10
+ }
+
+ Label {
+ text: qsTr("Rgb Value")
+ }
+
+ TextField {
+ id: colorRGBField
+ text: colorEditor.currentColor.toString()
+ readOnly: true
+ padding: 10
+ }
+ }
+
+ Button {
+ id: colordialogButton
+ Layout.fillWidth: true
+ Layout.preferredHeight: 30
+ text: qsTr("Set Color")
+ textColor: isColorDark(buttonColor) ? "#E6E6E6" : "#191919"
+
+ onClicked: colorDialog.open()
+
+ function isColorDark(color) {
+ return (0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b) < 0.5;
+ }
+ }
+
+ RowLayout {
+ Layout.fillWidth: true
+ spacing: 10
+
+ Button {
+ text: qsTr("Cancel")
+ onClicked: colorEditor.close()
+ Layout.fillWidth: true
+ }
+
+ Button {
+ Layout.fillWidth: true
+ text: colorEditor.newColor ? qsTr("Add") : qsTr("Update")
+
+ buttonColor: "#2CDE85"
+ textColor: "#FFFFFF"
+
+ onClicked: {
+ if (colorEditor.newColor) {
+ colorEditor.colorAdded(colorNameField.text,
+ colorRGBField.text,
+ colorPantoneField.text)
+ } else {
+ colorEditor.colorUpdated(colorNameField.text,
+ colorRGBField.text,
+ colorPantoneField.text,
+ colorEditor.colorId)
+ }
+ colorEditor.close()
+ }
+ }
+ }
+ }
+}
--- /dev/null
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQuick.Effects
+import QtQuick.Shapes
+
+import QtExampleStyle
+import ColorPalette
+
+Item {
+ id: root
+ required property BasicLogin loginService
+ required property PaginatedColorsResource colors
+ required property PaginatedColorUsersResource colorViewUsers
+
+ ColorDialogEditor {
+ id: colorPopup
+ onColorAdded: (colorNameField, colorRGBField, colorPantoneField) => {
+ root.colors.add({"name" : colorNameField,
+ "color" : colorRGBField,
+ "pantone_value" : colorPantoneField})
+ }
+
+ onColorUpdated: (colorNameField, colorRGBField, colorPantoneField, cid) => {
+ root.colors.update({"name" : colorNameField,
+ "color" : colorRGBField,
+ "pantone_value" : colorPantoneField},
+ cid)
+ }
+ }
+
+ ColorDialogDelete {
+ id: colorDeletePopup
+ onDeleteClicked: (cid) => {
+ root.colors.remove(cid)
+ }
+ }
+
+ ColumnLayout {
+ // The main application layout
+ anchors.fill :parent
+
+ ToolBar {
+ Layout.fillWidth: true
+ Layout.minimumHeight: 25 + 4
+
+ UserMenu {
+ id: userMenu
+
+ userMenuUsers: root.colorViewUsers
+ userLoginService: root.loginService
+ }
+
+ RowLayout {
+ anchors.fill: parent
+ Text {
+ text: qsTr("QHTTP Server")
+ font.pixelSize: 8
+ color: "#667085"
+ }
+ Item { Layout.fillWidth: true }
+
+ AbstractButton {
+ id: loginButton
+ Layout.preferredWidth: 25
+ Layout.preferredHeight: 25
+ Item {
+ id: userImageCliped
+ anchors.left: parent.left
+ anchors.verticalCenter: parent.verticalCenter
+ width: 25
+ height: 25
+
+ Image {
+ id: userImage
+ anchors.fill: parent
+ source: getCurrentUserImage()
+ visible: false
+
+ function getCurrentUserImage() {
+ if (root.loginService.loggedIn)
+ return users.avatarForEmail(loginService.user)
+ return "qrc:/qt/qml/ColorPalette/icons/user.svg";
+ }
+ }
+
+ Image {
+ id: userMask
+ source: "qrc:/qt/qml/ColorPalette/icons/userMask.svg"
+ anchors.fill: userImage
+ anchors.margins: 4
+ visible: false
+ }
+
+ MultiEffect {
+ source: userImage
+ anchors.fill: userImage
+ maskSource: userMask
+ maskEnabled: true
+ }
+ }
+
+ onClicked: {
+ userMenu.open()
+ var pos = mapToGlobal(Qt.point(x, y))
+ pos = userMenu.parent.mapFromGlobal(pos)
+ userMenu.x = x - userMenu.width + 25 + 3
+ userMenu.y = y + 25 + 3
+ }
+
+ Shape {
+ id: bubble
+ x: -text.width - 25
+ anchors.margins: 3
+
+ preferredRendererType: Shape.CurveRenderer
+
+ visible: !root.loginService.loggedIn
+
+ ShapePath {
+ strokeWidth: 0
+ fillColor: "#667085"
+ startX: 5; startY: 0
+ PathLine { x: 5 + text.width + 6; y: 0 }
+ PathArc { x: 10 + text.width + 6; y: 5; radiusX: 5; radiusY: 5}
+ // arrow
+ PathLine { x: 10 + text.width + 6; y: 8 + text.height / 2 - 6 }
+ PathLine { x: 10 + text.width + 6 + 6; y: 8 + text.height / 2 }
+ PathLine { x: 10 + text.width + 6; y: 8 + text.height / 2 + 6}
+ PathLine { x: 10 + text.width + 6; y: 5 + text.height + 6 }
+ // end arrow
+ PathArc { x: 5 + text.width + 6; y: 10 + text.height + 6 ; radiusX: 5; radiusY: 5}
+ PathLine { x: 5; y: 10 + text.height + 6 }
+ PathArc { x: 0; y: 5 + text.height + 6 ; radiusX: 5; radiusY: 5}
+ PathLine { x: 0; y: 5 }
+ PathArc { x: 5; y: 0 ; radiusX: 5; radiusY: 5}
+ }
+ Text {
+ x: 8
+ y: 8
+ id: text
+ color: "white"
+ text: qsTr("Log in to edit")
+ font.bold: true
+ horizontalAlignment: Qt.AlignHCenter
+ verticalAlignment: Qt.AlignVCenter
+ }
+ }
+ }
+ }
+
+ Image {
+ anchors.centerIn: parent
+ source: "qrc:/qt/qml/ColorPalette/icons/qt.png"
+ fillMode: Image.PreserveAspectFit
+ height: 25
+ }
+
+ }
+ ToolBar {
+ Layout.fillWidth: true
+ Layout.minimumHeight: 32
+
+ RowLayout {
+ anchors.fill: parent
+ Text {
+ Layout.alignment: Qt.AlignVCenter
+ text: qsTr("Color Palette")
+ font.pixelSize: 14
+ font.bold: true
+ color: "#667085"
+ }
+
+ Item { Layout.fillWidth: true }
+
+ AbstractButton {
+ Layout.preferredWidth: 25
+ Layout.preferredHeight: 25
+ Layout.alignment: Qt.AlignVCenter
+
+ Rectangle {
+ anchors.fill: parent
+ radius: 4
+ color: "#192CDE85"
+ border.color: "#DDE2E8"
+ border.width: 1
+ }
+
+ Image {
+ source: UIStyle.iconPath("plus")
+ fillMode: Image.PreserveAspectFit
+ anchors.fill: parent
+ sourceSize.width: width
+ sourceSize.height: height
+
+ }
+ visible: root.loginService.loggedIn
+ onClicked: colorPopup.createNewColor()
+ }
+
+ AbstractButton {
+ Layout.preferredWidth: 25
+ Layout.preferredHeight: 25
+ Layout.alignment: Qt.AlignVCenter
+
+ Rectangle {
+ anchors.fill: parent
+ radius: 4
+ color: "#192CDE85"
+ border.color: "#DDE2E8"
+ border.width: 1
+ }
+
+ Image {
+ source: UIStyle.iconPath("update")
+ fillMode: Image.PreserveAspectFit
+ anchors.fill: parent
+ sourceSize.width: width
+ sourceSize.height: height
+ }
+
+ onClicked: {
+ root.colors.refreshCurrentPage()
+ root.colorViewUsers.refreshCurrentPage()
+ }
+ }
+ }
+ }
+
+
+
+ //! [View and model]
+ ListView {
+ id: colorListView
+
+ model: root.colors.model
+ //! [View and model]
+ footerPositioning: ListView.OverlayFooter
+ spacing: 15
+ clip: true
+
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+
+ header: Rectangle {
+ height: 32
+ width: parent.width
+ color: "#F0F1F3"
+
+ RowLayout {
+ anchors.fill: parent
+
+ component HeaderText : Text {
+ Layout.alignment: Qt.AlignVCenter
+ horizontalAlignment: Qt.AlignHCenter
+
+ font.pixelSize: 12
+ color: "#667085"
+ }
+ HeaderText {
+ id: headerName
+ text: qsTr("Color Name")
+ Layout.preferredWidth: colorListView.width * 0.3
+ }
+ HeaderText {
+ id: headerRgb
+ text: qsTr("Rgb Value")
+ Layout.preferredWidth: colorListView.width * 0.25
+ }
+ HeaderText {
+ id: headerPantone
+ text: qsTr("Pantone Value")
+ Layout.preferredWidth: colorListView.width * 0.25
+ }
+ HeaderText {
+ id: headerAction
+ text: qsTr("Action")
+ Layout.preferredWidth: colorListView.width * 0.2
+ }
+ }
+ }
+
+ delegate: Item {
+ id: colorInfo
+
+ required property int color_id
+ required property string name
+ required property string color
+ required property string pantone_value
+
+ width: colorListView.width
+ height: 25
+ RowLayout {
+ anchors.fill: parent
+ anchors.leftMargin: 5
+ anchors.rightMargin: 5
+
+ Rectangle {
+ id: colorSample
+ Layout.alignment: Qt.AlignVCenter
+ implicitWidth: 36
+ implicitHeight: 21
+ radius: 6
+ color: colorInfo.color
+ }
+
+ Text {
+ Layout.preferredWidth: colorInfo.width * 0.3 - colorSample.width
+ horizontalAlignment: Qt.AlignLeft
+ leftPadding: 5
+ text: colorInfo.name
+ }
+
+ Text {
+ Layout.preferredWidth: colorInfo.width * 0.25
+ horizontalAlignment: Qt.AlignHCenter
+ text: colorInfo.color
+ }
+
+ Text {
+ Layout.preferredWidth: colorInfo.width * 0.25
+ horizontalAlignment: Qt.AlignHCenter
+ text: colorInfo.pantone_value
+ }
+
+ Item {
+ Layout.maximumHeight: 28
+ implicitHeight: buttonBox.implicitHeight
+ implicitWidth: buttonBox.implicitWidth
+
+ RowLayout {
+ id: buttonBox
+ anchors.fill: parent
+ ToolButton {
+ icon.source: UIStyle.iconPath("delete")
+ enabled: root.loginService.loggedIn
+ onClicked: colorDeletePopup.maybeDelete(color_id, name)
+ }
+ ToolButton {
+ icon.source: UIStyle.iconPath("edit")
+ enabled: root.loginService.loggedIn
+ onClicked: colorPopup.updateColor(color_id, name, color, pantone_value)
+ }
+ }
+ }
+ }
+ }
+
+ footer: ToolBar {
+ // Paginate buttons if more than one page
+ visible: root.colors.pages > 1
+ implicitWidth: parent.width
+
+ RowLayout {
+ anchors.fill: parent
+
+ Item { Layout.fillWidth: true /* spacer */ }
+
+ Repeater {
+ model: root.colors.pages
+
+ ToolButton {
+ text: page
+ font.bold: root.colors.page === page
+
+ required property int index
+ readonly property int page: (index + 1)
+
+ onClicked: root.colors.page = page
+ }
+ }
+ }
+ }
+ }
+ }
+}
--- /dev/null
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma ComponentBehavior: Bound
+
+import QtQuick
+
+import ColorPalette
+
+Window {
+ id: window
+ width: 500
+ height: 400
+ visible: true
+ title: qsTr("Color Palette Client")
+
+ enum DataView {
+ UserView = 0,
+ ColorView = 1
+ }
+
+ ServerSelection {
+ id: serverview
+ anchors.fill: parent
+ onServerSelected: {colorview.visible = true; serverview.visible = false}
+ colorResources: colors
+ restPalette: paletteService
+ colorUsers: users
+ }
+
+ ColorView {
+ id: colorview
+ anchors.fill: parent
+ visible: false
+ loginService: colorLogin
+ colors: colors
+ colorViewUsers: users
+ }
+
+ //! [RestService QML element]
+ RestService {
+ id: paletteService
+
+ PaginatedColorUsersResource {
+ id: users
+ path: "/api/users"
+ }
+
+ PaginatedColorsResource {
+ id: colors
+ path: "/api/unknown"
+ }
+
+ BasicLogin {
+ id: colorLogin
+ loginPath: "/api/login"
+ logoutPath: "/api/logout"
+ }
+ }
+ //! [RestService QML element]
+
+}
--- /dev/null
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+import ColorPalette
+import QtExampleStyle
+
+pragma ComponentBehavior: Bound
+
+Item {
+ id: root
+ // A popup for selecting the server URL
+
+ signal serverSelected()
+
+ required property PaginatedColorsResource colorResources
+ required property PaginatedColorUsersResource colorUsers
+ required property RestService restPalette
+
+ Connections {
+ target: root.colorResources
+ // Closes the URL selection popup once we have received data successfully
+ function onDataUpdated() {
+ fetchTester.stop()
+ root.serverSelected()
+ }
+ }
+
+
+ ListModel {
+ id: server
+ ListElement {
+ title: qsTr("Public REST API Test Server")
+ url: "https://reqres.in"
+ icon: "qrc:/qt/qml/ColorPalette/icons/testserver.png"
+ }
+ ListElement {
+ title: qsTr("Qt-based REST API server")
+ url: "http://127.0.0.1:49425"
+ icon: "qrc:/qt/qml/ColorPalette/icons/qt.png"
+ }
+ }
+
+
+ ColumnLayout {
+ anchors.fill: parent
+ anchors.margins: 20
+ spacing: 10
+
+ Image {
+ Layout.alignment: Qt.AlignHCenter
+ source: "qrc:/qt/qml/ColorPalette/icons/qt.png"
+ fillMode: Image.PreserveAspectFit
+ Layout.preferredWidth: 20
+ }
+
+ Label {
+ text: qsTr("Choose a server")
+ Layout.alignment: Qt.AlignHCenter
+ font.pixelSize: 24
+ }
+
+ component ServerListDelegate: Rectangle {
+ id: serverListDelegate
+ required property string title
+ required property string url
+ required property string icon
+ required property int index
+
+ radius: 10
+ color: "#00000000"
+
+ border.color: ListView.view.currentIndex === index ? "#2CDE85" : "#E0E2E7"
+ border.width: 2
+
+ implicitWidth: 180
+ implicitHeight: 100
+
+ Rectangle {
+ id: img
+ anchors.left: parent.left
+ anchors.top: parent.top
+ anchors.topMargin: 10
+ anchors.leftMargin: 20
+
+ width: 30
+ height: 30
+ radius: 200
+ border. color: "#E7F4EE"
+ border.width: 5
+
+ Image {
+ anchors.centerIn: parent
+ source: serverListDelegate.icon
+ width: 15
+ height: 15
+ fillMode: Image.PreserveAspectFit
+ smooth: true
+ }
+ }
+
+ Text {
+ text: parent.url
+
+ anchors.left: parent.left
+ anchors.top: img.bottom
+ anchors.topMargin: 10
+ anchors.leftMargin: 20
+ color: "#667085"
+ font.pixelSize: 13
+ }
+ Text {
+ text: parent.title
+
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: 10
+ color: "#222222"
+ font.pixelSize: 11
+ font.bold: true
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: serverList.currentIndex = serverListDelegate.index;
+ }
+ }
+
+ ListView {
+ id: serverList
+ Layout.alignment: Qt.AlignHCenter
+ Layout.minimumWidth: 180 * server.count + 20
+ Layout.minimumHeight: 100
+ orientation: ListView.Horizontal
+
+ model: server
+ spacing: 20
+
+ delegate: ServerListDelegate {}
+ }
+
+ Button {
+ Layout.alignment: Qt.AlignHCenter
+ text: restPalette.sslSupported ? qsTr("Connect (SSL)") : qsTr("Connect")
+
+ buttonColor: "#2CDE85"
+ textColor: "#FFFFFF"
+
+ onClicked: {
+ busyIndicatorPopup.title = (serverList.currentItem as ServerListDelegate).title
+ busyIndicatorPopup.icon = (serverList.currentItem as ServerListDelegate).icon
+ busyIndicatorPopup.open()
+
+ fetchTester.test((serverList.currentItem as ServerListDelegate).url)
+ }
+ }
+
+ Timer {
+ id: fetchTester
+ interval: 2000
+
+ function test(url) {
+ root.restPalette.url = url
+ root.colorResources.refreshCurrentPage()
+ root.colorUsers.refreshCurrentPage()
+ start()
+ }
+ onTriggered: busyIndicatorPopup.close()
+ }
+ }
+
+ onVisibleChanged: {if (!visible) busyIndicatorPopup.close();}
+
+ Popup {
+ id: busyIndicatorPopup
+ padding: 10
+ modal: true
+ focus: true
+ anchors.centerIn: parent
+ closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
+
+ property alias title: titleText.text
+ property alias icon: titleImg.source
+
+ ColumnLayout {
+ id: fetchIndicator
+ anchors.fill: parent
+
+ RowLayout {
+ Rectangle {
+ Layout.preferredWidth: 50
+ Layout.preferredHeight: 50
+ radius: 200
+ border. color: "#E7F4EE"
+ border.width: 5
+
+ Image {
+ id: titleImg
+ anchors.centerIn: parent
+ width: 25
+ height: 25
+ fillMode: Image.PreserveAspectFit
+ }
+ }
+
+ Label {
+ id: titleText
+ text:""
+ font.pixelSize: 18
+ }
+ }
+
+ RowLayout {
+ Layout.fillWidth: false
+ Layout.alignment: Qt.AlignHCenter
+ BusyIndicator {
+ running: visible
+ Layout.fillWidth: true
+ }
+
+ Label {
+ text: qsTr("Testing URL")
+ font.pixelSize: 18
+ }
+ }
+
+ Button {
+ Layout.alignment: Qt.AlignHCenter
+ text: qsTr("Cancel")
+ onClicked: {
+ busyIndicatorPopup.close()
+ }
+ }
+
+ }
+
+ }
+}
--- /dev/null
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQuick.Effects
+
+import QtExampleStyle
+import ColorPalette
+
+Popup {
+ id: userMenu
+
+ required property BasicLogin userLoginService
+ required property PaginatedColorUsersResource userMenuUsers
+
+ width: 280
+ height: 270
+
+ ColumnLayout {
+ anchors.fill: parent
+
+ ListView {
+ id: userListView
+
+ model: userMenu.userMenuUsers.model
+ spacing: 5
+ footerPositioning: ListView.PullBackFooter
+ clip: true
+
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+
+ delegate: Rectangle {
+ id: userInfo
+
+ required property string email
+ required property string avatar
+
+ height: 30
+ width: userListView.width
+
+
+ readonly property bool logged: (email === loginService.user)
+
+ Rectangle {
+ id: userImageCliped
+ anchors.left: parent.left
+ anchors.verticalCenter: parent.verticalCenter
+ width: 30
+ height: 30
+
+ Image {
+ id: userImage
+ anchors.fill: parent
+ source: userInfo.avatar
+ visible: false
+ }
+
+ Image {
+ id: userMask
+ source: "qrc:/qt/qml/ColorPalette/icons/userMask.svg"
+ anchors.fill: userImage
+ anchors.margins: 4
+ visible: false
+ }
+
+ MultiEffect {
+ source: userImage
+ anchors.fill: userImage
+ maskSource: userMask
+ maskEnabled: true
+ }
+ }
+
+ Text {
+ id: userMailLabel
+ anchors.left: userImageCliped.right
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.margins: 5
+ text: userInfo.email
+ font.bold: userInfo.logged
+ }
+
+ ToolButton {
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.margins: 5
+
+ icon.source: UIStyle.iconPath(userInfo.logged
+ ? "logout" : "login")
+ enabled: userInfo.logged || !userMenu.userLoginService.loggedIn
+
+ onClicked: {
+ if (userInfo.logged) {
+ userMenu.userLoginService.logout()
+ } else {
+ //! [Login]
+ userMenu.userLoginService.login({"email" : userInfo.email,
+ "password" : "apassword",
+ "id" : userInfo.id})
+ //! [Login]
+ userMenu.close()
+ }
+ }
+ }
+
+ }
+ footer: ToolBar {
+ // Paginate buttons if more than one page
+ visible: userMenu.userMenuUsers.pages > 1
+ implicitWidth: parent.width
+
+ RowLayout {
+ anchors.fill: parent
+
+ Item { Layout.fillWidth: true /* spacer */ }
+
+ Repeater {
+ model: userMenu.userMenuUsers.pages
+
+ ToolButton {
+ text: page
+ font.bold: userMenu.userMenuUsers.page === page
+
+ required property int index
+ readonly property int page: (index + 1)
+
+ onClicked: userMenu.userMenuUsers.page = page
+ }
+ }
+ }
+ }
+ }
+ }
+}
--- /dev/null
+module ColorPalette
+Main 1.0 Main.qml
+ColorDialogDelete 1.0 ColorDialogDelete.qml
+ColorDialogEditor 1.0 ColorDialogEditor.qml
+ColorView 1.0 ColorView.qml
+ServerSelection 1.0 ServerSelection.qml
+UserMenu 1.0 UserMenu.qml
--- /dev/null
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.impl
+import QtQuick.Templates as T
+
+T.Button {
+ id: control
+
+ property alias buttonColor: rect.color
+ property alias textColor: label.color
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding)
+
+ leftPadding: 15
+ rightPadding: 15
+ topPadding: 10
+ bottomPadding: 10
+
+ background: Rectangle {
+ id: rect
+ radius: 8
+ border.color: "#E0E2E7"
+ border.width: 1
+ color: "#FFFFFF"
+ }
+
+ icon.width: 24
+ icon.height: 24
+ icon.color: control.palette.buttonText
+
+ contentItem: IconLabel {
+ id: label
+ spacing: control.spacing
+ mirrored: control.mirrored
+ display: control.display
+
+ icon: control.icon
+ text: control.text
+ font.pixelSize: 14
+ color: "#667085"
+ }
+}
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(qtexamplestyle LANGUAGES CXX)
+
+set(CMAKE_AUTOMOC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quickcontrols/colorpaletteclient/QtExampleStyle")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick QuickControls2)
+
+set_source_files_properties(UIStyle.qml
+ PROPERTIES
+ QT_QML_SINGLETON_TYPE TRUE
+)
+
+qt_policy(SET QTP0001 NEW)
+qt_add_qml_module(qtexamplestyle
+ URI QtExampleStyle
+ PLUGIN_TARGET qtexamplestyle
+ QML_FILES
+ Button.qml
+ Popup.qml
+ UIStyle.qml
+ TextField.qml
+)
+
+target_link_libraries(qtexamplestyle PUBLIC
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Quick
+ Qt6::QuickControls2
+)
+
+if(UNIX AND NOT APPLE AND CMAKE_CROSSCOMPILING)
+ find_package(Qt6 REQUIRED COMPONENTS QuickTemplates2)
+
+ # Work around QTBUG-86533
+ target_link_libraries(qtexamplestyle PRIVATE Qt6::QuickTemplates2)
+endif()
+
+install(TARGETS qtexamplestyle
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qmldir
+ DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
--- /dev/null
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Templates as T
+
+T.Popup {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding)
+
+ leftPadding: 15
+ rightPadding: 15
+ topPadding: 10
+ bottomPadding: 10
+
+ background: Rectangle {
+ id: bg
+ radius: 8
+ border.color: "#E0E2E7"
+ border.width: 2
+ color: "#FFFFFF"
+ }
+}
--- /dev/null
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Templates as T
+
+T.TextField {
+ id: control
+ placeholderText: ""
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, contentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ contentHeight + topPadding + bottomPadding)
+
+ background: Rectangle {
+ implicitWidth: 200
+ implicitHeight: 40
+ radius: 8
+ color: control.enabled ? "transparent" : "#353637"
+ border.color: "#E0E2E7"
+ }
+}
--- /dev/null
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma Singleton
+
+import QtQuick
+
+QtObject {
+ id: uiStyle
+
+ // Font Sizes
+ readonly property int fontSizeXXS: 10
+ readonly property int fontSizeXS: 15
+ readonly property int fontSizeS: 20
+ readonly property int fontSizeM: 25
+ readonly property int fontSizeL: 30
+ readonly property int fontSizeXL: 35
+ readonly property int fontSizeXXL: 40
+
+ // Color Scheme
+ // Green
+ readonly property color colorQtPrimGreen: "#41cd52"
+ readonly property color colorQtAuxGreen1: "#21be2b"
+ readonly property color colorQtAuxGreen2: "#17a81a"
+
+ function iconPath(baseImagePath) {
+ return `qrc:/qt/qml/ColorPalette/icons/${baseImagePath}.svg`
+ }
+}
--- /dev/null
+module QtExampleStyle
+Button 1.0 Button.qml
+Popup 1.0 Popup.qml
+TextField 1.0 TextField.qml
+singleton UIStyle 1.0 UIStyle.qml
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import QObject
+from PySide6.QtQml import QmlAnonymous
+
+
+QML_IMPORT_NAME = "ColorPalette"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlAnonymous
+class AbstractResource(QObject):
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.m_manager = None # QRestAccessManager
+ self.m_api = None # QNetworkRequestFactory
+
+ def setAccessManager(self, manager):
+ self.m_manager = manager
+
+ def setServiceApi(self, serviceApi):
+ self.m_api = serviceApi
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import sys
+from functools import partial
+from dataclasses import dataclass
+
+from PySide6.QtCore import Property, Signal, Slot
+from PySide6.QtNetwork import QHttpHeaders
+from PySide6.QtQml import QmlElement
+
+from abstractresource import AbstractResource
+
+
+tokenField = "token"
+emailField = "email"
+idField = "id"
+
+
+QML_IMPORT_NAME = "ColorPalette"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class BasicLogin(AbstractResource):
+ @dataclass
+ class User:
+ email: str
+ token: bytes
+ id: int
+
+ userChanged = Signal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.m_user = None
+ self.m_loginPath = ""
+ self.m_logoutPath = ""
+ self.m_user = None
+
+ @Property(str, notify=userChanged)
+ def user(self):
+ return self.m_user.email if self.m_user else ""
+
+ @Property(bool, notify=userChanged)
+ def loggedIn(self):
+ return bool(self.m_user)
+
+ @Property(str)
+ def loginPath(self):
+ return self.m_loginPath
+
+ @loginPath.setter
+ def loginPath(self, p):
+ self.m_loginPath = p
+
+ @Property(str)
+ def logoutPath(self):
+ return self.m_logoutPath
+
+ @logoutPath.setter
+ def logoutPath(self, p):
+ self.m_logoutPath = p
+
+ @Slot("QVariantMap")
+ def login(self, data):
+ request = self.m_api.createRequest(self.m_loginPath)
+ self.m_manager.post(request, data, self, partial(self.loginReply, data))
+
+ def loginReply(self, data, reply):
+ self.m_user = None
+ if not reply.isSuccess():
+ print("login: ", reply.errorString(), file=sys.stderr)
+ (json, error) = reply.readJson()
+ if json and json.isObject():
+ json_object = json.object()
+ token = json_object.get(tokenField)
+ if token:
+ email = data[emailField]
+ token = json_object[tokenField]
+ id = data[idField]
+ self.m_user = BasicLogin.User(email, token, id)
+
+ headers = QHttpHeaders()
+ headers.append("token", self.m_user.token if self.m_user else "")
+ self.m_api.setCommonHeaders(headers)
+ self.userChanged.emit()
+
+ @Slot()
+ def logout(self):
+ request = self.m_api.createRequest(self.m_logoutPath)
+ self.m_manager.post(request, b"", self, self.logoutReply)
+
+ def logoutReply(self, reply):
+ if reply.isSuccess():
+ self.m_user = None
+ self.m_api.clearCommonHeaders() # clears 'token' header
+ self.userChanged.emit()
+ else:
+ print("logout: ", reply.errorString(), file=sys.stderr)
--- /dev/null
+{
+ "files": [
+ "abstractresource.py",
+ "basiclogin.py",
+ "main.py",
+ "paginatedresource.py",
+ "restservice.py",
+ "colorpaletteclient.qrc",
+ "ColorPalette/ColorDialogDelete.qml",
+ "ColorPalette/ColorDialogEditor.qml",
+ "ColorPalette/ColorView.qml",
+ "ColorPalette/Main.qml",
+ "ColorPalette/ServerSelection.qml",
+ "ColorPalette/UserMenu.qml",
+ "QtExampleStyle/Button.qml",
+ "QtExampleStyle/Popup.qml",
+ "QtExampleStyle/TextField.qml",
+ "QtExampleStyle/UIStyle.qml",
+ "colorpaletteclient.qrc"
+ ]
+}
--- /dev/null
+<RCC>
+ <qresource prefix="/qt/qml/ColorPalette">
+ <file>icons/close.svg</file>
+ <file>icons/delete.svg</file>
+ <file>icons/dots.svg</file>
+ <file>icons/edit.svg</file>
+ <file>icons/login.svg</file>
+ <file>icons/logout.svg</file>
+ <file>icons/ok.svg</file>
+ <file>icons/plus.svg</file>
+ <file>icons/qt.png</file>
+ <file>icons/testserver.png</file>
+ <file>icons/update.svg</file>
+ <file>icons/user.svg</file>
+ <file>icons/userMask.svg</file>
+ </qresource>
+</RCC>
--- /dev/null
+RESTful API client
+==================
+
+Example of how to create a RESTful API QML client.
+
+This example shows how to create a basic QML RESTful API client with an
+imaginary color palette service. The application uses RESTful communication
+with the selected server to request and send data. The REST service is provided
+as a QML element whose child elements wrap the individual JSON data APIs
+provided by the server.
+
+Application functionality
+-------------------------
+
+The example provides the following basic functionalities:
+* Select the server to communicate with
+* List users and colors
+* Login and logout users
+* Modify and create new colors
+
+Server selection
+----------------
+
+At start the application presents the options for the color palette server to communicate
+with. The predefined options are:
+
+* ``https://reqres.in``, a publicly available REST API test service
+* A Qt-based REST API server example in ``QtHttpServer``
+
+Once selected, the RESTful API client issues a test HTTP GET to the color API
+to check if the service is accessible.
+
+One major difference between the two predefined API options is that the
+Qt-based REST API server example is a stateful application which allows
+modifying colors, whereas the ``reqres.in`` is a stateless API testing service.
+In other words, when using the ``reqres.in`` backend, modifying the colors has
+no lasting impact.
+
+The users and colors are paginated resources on the server-side. This means
+that the server provides the data in chunks called pages. The UI listing
+reflects this pagination and views the data on pages.
+
+Viewing the data on UI is done with standard QML views where the model are
+QAbstractListModel-derived classes representing JSON data received from the
+server.
+
+Logging in happens via the login function provided by the login popup. Under
+the hood the login sends a HTTP POST request. Upon receiving a successful
+response the authorization token is extracted from the response, which in turn
+is then used in subsequent HTTP requests which require the token.
+
+Editing and adding new colors is done in a popup. Note that uploading the color
+changes to the server requires that a user has logged in.
+
+REST implementation
+-------------------
+
+The example illustrates one way to compose a REST service from individual resource elements. In
+this example the resources are the paginated user and color resources plus the login service.
+The resource elements are bound together by the base URL (server URL) and the shared network access
+manager.
+
+The basis of the REST service is the RestService QML element whose children items
+compose the actual service.
+
+Upon instantiation the RestService element loops its children elements and sets
+them up to use the same network access manager. This way the individual
+resources share the same access details such as the server URL and
+authorization token.
+
+The actual communication is done with a rest access manager which implements
+some convenience functionality to deal specifically with HTTP REST APIs and
+effectively deals with sending and receiving the ``QNetworkRequest`` and
+``QNetworkReply`` as needed.
+
+.. image:: colorpaletteclient.webp
+ :width: 90%
+ :align: center
+ :alt: RESTful API client
--- /dev/null
+<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M12.4501 37.65L10.3501 35.55L21.9001 24L10.3501 12.45L12.4501 10.35L24.0001 21.9L35.5501 10.35L37.6501 12.45L26.1001 24L37.6501 35.55L35.5501 37.65L24.0001 26.1L12.4501 37.65Z" fill="#667085"/>
+</svg>
--- /dev/null
+<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M13.05 42C12.225 42 11.5187 41.7062 10.9313 41.1188C10.3438 40.5312 10.05 39.825 10.05 39V10.5H8V7.5H17.4V6H30.6V7.5H40V10.5H37.95V39C37.95 39.8 37.65 40.5 37.05 41.1C36.45 41.7 35.75 42 34.95 42H13.05ZM34.95 10.5H13.05V39H34.95V10.5ZM18.35 34.7H21.35V14.75H18.35V34.7ZM26.65 34.7H29.65V14.75H26.65V34.7Z" fill="#667085"/>
+</svg>
--- /dev/null
+<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M10.3929 26.4C9.73097 26.4 9.16667 26.1643 8.7 25.6929C8.23333 25.2215 8 24.6548 8 23.9929C8 23.3309 8.2357 22.7666 8.7071 22.3C9.17847 21.8333 9.74513 21.6 10.4071 21.6C11.069 21.6 11.6333 21.8357 12.1 22.3071C12.5667 22.7784 12.8 23.3451 12.8 24.0071C12.8 24.669 12.5643 25.2333 12.0929 25.7C11.6215 26.1666 11.0549 26.4 10.3929 26.4ZM23.9929 26.4C23.331 26.4 22.7667 26.1643 22.3 25.6929C21.8333 25.2215 21.6 24.6548 21.6 23.9929C21.6 23.3309 21.8357 22.7666 22.3071 22.3C22.7785 21.8333 23.3451 21.6 24.0071 21.6C24.669 21.6 25.2333 21.8357 25.7 22.3071C26.1667 22.7784 26.4 23.3451 26.4 24.0071C26.4 24.669 26.1643 25.2333 25.6929 25.7C25.2215 26.1666 24.6549 26.4 23.9929 26.4ZM37.5929 26.4C36.931 26.4 36.3667 26.1643 35.9 25.6929C35.4333 25.2215 35.2 24.6548 35.2 23.9929C35.2 23.3309 35.4357 22.7666 35.9071 22.3C36.3785 21.8333 36.9451 21.6 37.6071 21.6C38.269 21.6 38.8333 21.8357 39.3 22.3071C39.7667 22.7784 40 23.3451 40 24.0071C40 24.669 39.7643 25.2333 39.2929 25.7C38.8215 26.1666 38.2549 26.4 37.5929 26.4Z" fill="#667085"/>
+</svg>
--- /dev/null
+<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M9 39H11.2L33.35 16.85L31.15 14.65L9 36.8V39ZM39.7 14.7L33.3 8.29998L35.4 6.19998C35.9667 5.63331 36.6667 5.34998 37.5 5.34998C38.3333 5.34998 39.0333 5.63331 39.6 6.19998L41.8 8.39998C42.3667 8.96664 42.65 9.66664 42.65 10.5C42.65 11.3333 42.3667 12.0333 41.8 12.6L39.7 14.7ZM37.6 16.8L12.4 42H6V35.6L31.2 10.4L37.6 16.8ZM32.25 15.75L31.15 14.65L33.35 16.85L32.25 15.75Z" fill="#667085"/>
+</svg>
--- /dev/null
+<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M24.45 42V39H39V9H24.45V6H39C39.8 6 40.5 6.3 41.1 6.9C41.7 7.5 42 8.2 42 9V39C42 39.8 41.7 40.5 41.1 41.1C40.5 41.7 39.8 42 39 42H24.45ZM20.55 32.75L18.4 30.6L23.5 25.5H6V22.5H23.4L18.3 17.4L20.45 15.25L29.25 24.05L20.55 32.75Z" fill="#667085"/>
+</svg>
--- /dev/null
+<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M9 42C8.2 42 7.5 41.7 6.9 41.1C6.3 40.5 6 39.8 6 39V9C6 8.2 6.3 7.5 6.9 6.9C7.5 6.3 8.2 6 9 6H23.55V9H9V39H23.55V42H9ZM33.3 32.75L31.15 30.6L36.25 25.5H18.75V22.5H36.15L31.05 17.4L33.2 15.25L42 24.05L33.3 32.75Z" fill="#667085"/>
+</svg>
--- /dev/null
+<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M18.9002 35.7L7.7002 24.5L9.8502 22.35L18.9002 31.4L38.1002 12.2L40.2502 14.35L18.9002 35.7Z" fill="#667085"/>
+</svg>
--- /dev/null
+<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M22.5 38V25.5H10V22.5H22.5V10H25.5V22.5H38V25.5H25.5V38H22.5Z" fill="#667085"/>
+</svg>
--- /dev/null
+{
+ "Id": "colorpaletteclient",
+ "Name": "Selected Material Icons",
+ "QDocModule": "qtdoc",
+ "QtUsage": "Used in Color Palette Client example in QtDoc",
+ "QtParts": [
+ "examples"
+ ],
+ "Files": "close.svg delete.svg dots.svg edit.svg login.svg logout.svg ok.svg update.svg user.svg",
+ "Homepage": "https://fonts.google.com/icons",
+ "License": "Apache License Version 2.0",
+ "LicenseId": "Apache-2.0",
+ "Copyright": "Copyright 2018 Google, Inc. All Rights Reserved."
+}
--- /dev/null
+<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M24 40C19.5667 40 15.7917 38.4417 12.675 35.325C9.55833 32.2083 8 28.4333 8 24C8 19.5667 9.55833 15.7917 12.675 12.675C15.7917 9.55833 19.5667 8 24 8C26.8333 8 29.3167 8.575 31.45 9.725C33.5833 10.875 35.4333 12.45 37 14.45V8H40V20.7H27.3V17.7H35.7C34.4333 15.7 32.8167 14.0833 30.85 12.85C28.8833 11.6167 26.6 11 24 11C20.3667 11 17.2917 12.2583 14.775 14.775C12.2583 17.2917 11 20.3667 11 24C11 27.6333 12.2583 30.7083 14.775 33.225C17.2917 35.7417 20.3667 37 24 37C26.7667 37 29.3 36.2083 31.6 34.625C33.9 33.0417 35.5 30.95 36.4 28.35H39.5C38.5333 31.85 36.6167 34.6667 33.75 36.8C30.8833 38.9333 27.6333 40 24 40Z" fill="#667085"/>
+</svg>
--- /dev/null
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M0 12C0 5.37258 5.37258 0 12 0C18.6274 0 24 5.37258 24 12C24 18.6274 18.6274 24 12 24C5.37258 24 0 18.6274 0 12Z" fill="#E6E6E6"/>
+<path d="M15.5 12C16.3284 12 17 12.6716 17 13.5V14C17 15.9714 15.1405 18 12 18C8.85951 18 7 15.9714 7 14V13.5C7 12.6716 7.67157 12 8.5 12H15.5ZM15.5 13H8.5C8.22386 13 8 13.2239 8 13.5V14C8 15.4376 9.43216 17 12 17C14.5678 17 16 15.4376 16 14V13.5C16 13.2239 15.7761 13 15.5 13ZM12 5.5C13.5188 5.5 14.75 6.73122 14.75 8.25C14.75 9.76878 13.5188 11 12 11C10.4812 11 9.25 9.76878 9.25 8.25C9.25 6.73122 10.4812 5.5 12 5.5ZM12 6.5C11.0335 6.5 10.25 7.2835 10.25 8.25C10.25 9.2165 11.0335 10 12 10C12.9665 10 13.75 9.2165 13.75 8.25C13.75 7.2835 12.9665 6.5 12 6.5Z" fill="#616161"/>
+</svg>
--- /dev/null
+<svg width="30" height="30" viewBox="0 0 30 30" xmlns="http://www.w3.org/2000/svg">
+<ellipse cx="15" cy="15" rx="13" ry="13" fill="black"/>
+</svg>
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+"""PySide6 port of the Qt RESTful API client demo from Qt v6.x"""
+
+import os
+import sys
+from pathlib import Path
+
+from PySide6.QtCore import QUrl
+from PySide6.QtGui import QIcon, QGuiApplication
+from PySide6.QtQml import QQmlApplicationEngine
+
+from basiclogin import BasicLogin # noqa: F401
+from paginatedresource import PaginatedResource # noqa: F401
+from restservice import RestService # noqa: F401
+import rc_colorpaletteclient # noqa: F401
+
+if __name__ == "__main__":
+ app = QGuiApplication(sys.argv)
+ QIcon.setThemeName("colorpaletteclient")
+
+ engine = QQmlApplicationEngine()
+ app_dir = Path(__file__).parent
+ app_dir_url = QUrl.fromLocalFile(os.fspath(app_dir))
+ engine.addImportPath(os.fspath(app_dir))
+ engine.loadFromModule("ColorPalette", "Main")
+ if not engine.rootObjects():
+ sys.exit(-1)
+
+ ex = app.exec()
+ del engine
+ sys.exit(ex)
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import sys
+from dataclasses import dataclass
+from PySide6.QtCore import (QAbstractListModel, QByteArray,
+ QUrlQuery, Property, Signal, Slot, Qt)
+from PySide6.QtQml import QmlAnonymous, QmlElement
+
+from abstractresource import AbstractResource
+
+
+QML_IMPORT_NAME = "ColorPalette"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+totalPagesField = "total_pages"
+currentPageField = "page"
+
+
+@dataclass
+class ColorUser:
+ id: int
+ email: str
+ avatar: str # URL
+
+
+@QmlElement
+class ColorUserModel (QAbstractListModel):
+ IdRole = Qt.UserRole + 1
+ EmailRole = Qt.UserRole + 2
+ AvatarRole = Qt.UserRole + 3
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._users = []
+
+ def clear(self):
+ self.set_data([])
+
+ def set_data(self, json_list):
+ if not self._users and not json_list:
+ return
+ self.beginResetModel()
+ self._users.clear()
+ for e in json_list:
+ self._users.append(ColorUser(int(e["id"]), e["email"], e["avatar"]))
+ self.endResetModel()
+
+ def roleNames(self):
+ roles = {
+ ColorUserModel.IdRole: QByteArray(b'id'),
+ ColorUserModel.EmailRole: QByteArray(b'email'),
+ ColorUserModel.AvatarRole: QByteArray(b'avatar')
+ }
+ return roles
+
+ def rowCount(self, index):
+ return len(self._users)
+
+ def data(self, index, role):
+ if index.isValid():
+ d = self._users[index.row()]
+ if role == ColorUserModel.IdRole:
+ return d.id
+ if role == ColorUserModel.EmailRole:
+ return d.email
+ if role == ColorUserModel.AvatarRole:
+ return d.avatar
+ return None
+
+ def avatarForEmail(self, email):
+ for e in self._users:
+ if e.email == email:
+ return e.avatar
+ return ""
+
+
+@dataclass
+class Color:
+ id: int
+ color: str
+ name: str
+ pantone_value: str
+
+
+@QmlElement
+class ColorModel (QAbstractListModel):
+ IdRole = Qt.UserRole + 1
+ ColorRole = Qt.UserRole + 2
+ NameRole = Qt.UserRole + 3
+ PantoneValueRole = Qt.UserRole + 4
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._colors = []
+
+ def clear(self):
+ self.set_data([])
+
+ def set_data(self, json_list):
+ if not self._colors and not json_list:
+ return
+ self.beginResetModel()
+ self._colors.clear()
+ for e in json_list:
+ self._colors.append(Color(int(e["id"]), e["color"],
+ e["name"], e["pantone_value"]))
+ self.endResetModel()
+
+ def roleNames(self):
+ roles = {
+ ColorModel.IdRole: QByteArray(b'color_id'),
+ ColorModel.ColorRole: QByteArray(b'color'),
+ ColorModel.NameRole: QByteArray(b'name'),
+ ColorModel.PantoneValueRole: QByteArray(b'pantone_value')
+ }
+ return roles
+
+ def rowCount(self, index):
+ return len(self._colors)
+
+ def data(self, index, role):
+ if index.isValid():
+ d = self._colors[index.row()]
+ if role == ColorModel.IdRole:
+ return d.id
+ if role == ColorModel.ColorRole:
+ return d.color
+ if role == ColorModel.NameRole:
+ return d.name
+ if role == ColorModel.PantoneValueRole:
+ return d.pantone_value
+ return None
+
+
+@QmlAnonymous
+class PaginatedResource(AbstractResource):
+ """This class manages a simple paginated Crud resource,
+ where the resource is a paginated list of JSON items."""
+
+ dataUpdated = Signal()
+ pageUpdated = Signal()
+ pagesUpdated = Signal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ # The total number of pages as reported by the server responses
+ self.m_pages = 0
+ # The default page we request if the user hasn't set otherwise
+ self.m_currentPage = 1
+ self.m_path = ""
+
+ def _clearModel(self):
+ pass
+
+ def _populateModel(self, json_list):
+ pass
+
+ @Property(str)
+ def path(self):
+ return self.m_path
+
+ @path.setter
+ def path(self, p):
+ self.m_path = p
+
+ @Property(int, notify=pagesUpdated)
+ def pages(self):
+ return self.m_pages
+
+ @Property(int, notify=pageUpdated)
+ def page(self):
+ return self.m_currentPage
+
+ @page.setter
+ def page(self, page):
+ if self.m_currentPage == page or page < 1:
+ return
+ self.m_currentPage = page
+ self.pageUpdated.emit()
+ self.refreshCurrentPage()
+
+ @Slot()
+ def refreshCurrentPage(self):
+ query = QUrlQuery()
+ query.addQueryItem("page", str(self.m_currentPage))
+ request = self.m_api.createRequest(self.m_path, query)
+ self.m_manager.get(request, self, self.refreshCurrentPageReply)
+
+ def refreshCurrentPageReply(self, reply):
+ if not reply.isSuccess():
+ print("PaginatedResource: ", reply.errorString(), file=sys.stderr)
+ (json, error) = reply.readJson()
+ if json:
+ self.refreshRequestFinished(json)
+ else:
+ self.refreshRequestFailed()
+
+ def refreshRequestFinished(self, json):
+ json_object = json.object()
+ self._populateModel(json_object["data"])
+ self.m_pages = int(json_object[totalPagesField])
+ self.m_currentPage = int(json_object[currentPageField])
+ self.pageUpdated.emit()
+ self.pagesUpdated.emit()
+ self.dataUpdated.emit()
+
+ def refreshRequestFailed(self):
+ if self.m_currentPage != 1:
+ # A failed refresh. If we weren't on page 1, try that.
+ # Last resource on currentPage might have been deleted, causing a failure
+ self.setPage(1)
+ else:
+ # Refresh failed and we we're already on page 1 => clear data
+ self.m_pages = 0
+ self.pagesUpdated.emit()
+ self._clearModel()
+ self.dataUpdated.emit()
+
+ @Slot("QVariantMap", int)
+ def update(self, data, id):
+ request = self.m_api.createRequest(f"{self.m_path}/{id}")
+ self.m_manager.put(request, self, self.updateReply)
+
+ def updateReply(self, reply):
+ if reply.isSuccess():
+ self.refreshCurrentPage()
+
+ @Slot("QVariantMap")
+ def add(self, data):
+ request = self.m_api.createRequest(self.m_path)
+ self.m_manager.post(request, data, self, self.updateReply)
+
+ @Slot(int)
+ def remove(self, id):
+ request = self.m_api.createRequest(f"{self.m_path}/{id}")
+ self.m_manager.deleteResource(request, self, self.updateReply)
+
+
+@QmlElement
+class PaginatedColorUsersResource(PaginatedResource):
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.m_model = ColorUserModel(self)
+
+ @Property(ColorUserModel, constant=True)
+ def model(self):
+ return self.m_model
+
+ def _clearModel(self):
+ self.m_model.clear()
+
+ def _populateModel(self, json_list):
+ self.m_model.set_data(json_list)
+
+ @Slot(str, result=str)
+ def avatarForEmail(self, email):
+ return self.m_model.avatarForEmail(email)
+
+
+@QmlElement
+class PaginatedColorsResource(PaginatedResource):
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.m_model = ColorModel(self)
+
+ @Property(ColorModel, constant=True)
+ def model(self):
+ return self.m_model
+
+ def _clearModel(self):
+ self.m_model.clear()
+
+ def _populateModel(self, json_list):
+ self.m_model.set_data(json_list)
--- /dev/null
+# Resource object code (Python 3)
+# Created by: object code
+# Created by: The Resource Compiler for Qt version 6.7.0
+# WARNING! All changes made in this file will be lost!
+
+from PySide6 import QtCore
+
+qt_resource_data = b"\
+\x00\x00\x00\xc0\
+<\
+svg width=\x2248\x22 h\
+eight=\x2248\x22 viewB\
+ox=\x220 0 48 48\x22 f\
+ill=\x22none\x22 xmlns\
+=\x22http://www.w3.\
+org/2000/svg\x22>\x0a<\
+path d=\x22M22.5 38\
+V25.5H10V22.5H22\
+.5V10H25.5V22.5H\
+38V25.5H25.5V38H\
+22.5Z\x22 fill=\x22#66\
+7085\x22/>\x0a</svg>\x0a\
+\x00\x00\x00\x94\
+<\
+svg width=\x2230\x22 h\
+eight=\x2230\x22 viewB\
+ox=\x220 0 30 30\x22 x\
+mlns=\x22http://www\
+.w3.org/2000/svg\
+\x22>\x0a<ellipse cx=\x22\
+15\x22 cy=\x2215\x22 rx=\x22\
+13\x22 ry=\x2213\x22 fill\
+=\x22black\x22/>\x0a</svg\
+>\x0a\x0a\
+\x00\x00\x0b\x93\
+\x89\
+PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
+\x00\x00d\x00\x00\x00H\x08\x06\x00\x00\x00\x00\x8cP\x19\
+\x00\x00\x01\x84iCCPICC prof\
+ile\x00\x00(\x91}\x91=H\xc3@\x1c\xc5_\
+S\xa5*-\x0ev\x90\xe2\x90\xa1:Y\x10\x15q\xd4\
+*\x14\xa1B\xa8\x15Zu0\xb9\xf4\x0b\x9a\x18\x92\x14\
+\x17G\xc1\xb5\xe0\xe0\xc7b\xd5\xc1\xc5YW\x07WA\
+\x10\xfc\x00quqRt\x91\x12\xff\x97\x14Z\xc4x\
+p\xdc\x8fw\xf7\x1ew\xef\x00\xa1Qe\x9a\xd55\x06\
+h\xbamfRI1\x97_\x11C\xaf\x08#\x82^\
+\xc4\x11\x93\x99e\xccJR\x1a\xbe\xe3\xeb\x1e\x01\xbe\xde\
+%x\x96\xff\xb9?GD-X\x0c\x08\x88\xc43\xcc\
+0m\xe2u\xe2\xa9M\xdb\xe0\xbcO\x1ceeY%\
+>'\x1e5\xe9\x82\xc4\x8f\x5cW<~\xe3\x5crY\
+\xe0\x99Q3\x9b\x99#\x8e\x12\x8b\xa5\x0eV:\x98\x95\
+M\x8dx\x928\xaej:\xe5\x0b9\x8fU\xce[\x9c\
+\xb5j\x8d\xb5\xee\xc9_\x18.\xe8\xcbK\x5c\xa79\x84\
+\x14\x16\xb0\x08\x09\x22\x14\xd4PA\x156\x12\xb4\xea\xa4\
+X\xc8\xd0~\xd2\xc7\x1fs\xfd\x12\xb9\x14rU\xc0\xc8\
+1\x8f\x0dh\x90]?\xf8\x1f\xfc\xee\xd6*N\x8c{\
+I\xe1$\xd0\xfd\xe28\x1f\xc3@h\x17h\xd6\x1d\xe7\
+\xfb\xd8q\x9a'@\xf0\x19\xb8\xd2\xdb\xfe\x8d\x060\xfd\
+Iz\xbd\xad\xc5\x8f\x80\xfem\xe0\xe2\xba\xad){\xc0\
+\xe5\x0e0\xf8d\xc8\xa6\xecJA\x9aB\xb1\x08\xbc\x9f\
+\xd17\xe5\x81\x81[\xa0o\xd5\xeb\xad\xb5\x8f\xd3\x07 \
+K]\xa5o\x80\x83C`\xa4D\xd9k>\xef\xee\xe9\
+\xec\xed\xdf3\xad\xfe~\x00a\xaer\xa0\xbc\xa9O\xc0\
+\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\
+\xa7\x93\x00\x00\x00\x09pHYs\x00\x00.#\x00\x00\
+.#\x01x\xa5?v\x00\x00\x00\x07tIME\x07\
+\xe7\x0c\x0d\x09$9Q3\xe6g\x00\x00\x00\x19tE\
+XtComment\x00Create\
+d with GIMPW\x81\x0e\x17\x00\
+\x00\x09kIDATx\xda\xed]ilT\xd7\x19\
+=\xe7\xbe\x99\xb1\xcd\x1aC\xc0\xac\xb6\xb1\x81\xda\x1eC\
+T\x11\x10\xa4i\x135\x91\x12E\xa4KT\x14\xda&\
+$]\x14W(\xa9\x02\x81\x006\x84Q\xf0B\x96\x06\
+*D\xd5\xf6\x17\x0a\xaa\xda\x84F)R\xc2\xa2$\x85\
+&M\xd3\x85%\xc2cV\x8f1aKR\xb0qX\
+\xec\x99y\xef~\xfdAAN\xeb\xfb\xbc\xf0f\xf0x\
+\xe6\xfe\x9b\xf9\xde\xdc\xef\xbe{\xde\xf9\xb6{\xef\x1b\xa2\
+\x87\xad\xbc\xf9\x85Rh{'\x80\xf1\xc8\xb4\x844\x81\
+\x08{\x04F\xa4\xfav\xa1\xbcA\xa8\xfc\xcc\xb4%\x12\
+\x10\xe8n\x01\x09\x9e\xa8-\xa3-;AL\xc8LY\
+\xe2\x01Q\xdd1\x03\x8e\xb3=\x03F\xf2\x9a\x91!e\
+\xc7\xd6\x06\x95\xa5w\x00\x92\x01\xe3f3$\x18y~\
+&\xad\xf8\xb6\x0c\x18\xfd\x80!e'\xd6\x06\x95\xed\xec\
+\x043\xd1\xd4MgH0\xf2\xfcL\xda\xf6\xf6\x0c\x18\
+\xfd\x80!\xa5\x8d5\xe5\x16\xb1#\x03F?`H0\
+R7SQ\xb6e\xc0\xe8\x07\x0c)\xfd\xa4\xa6\x5c\xd9\
+\xd8I`\xdcM}:D\x84\x8a\xed\x10DE\xc4\x01\
+\x00\x92>\x00\xd9\x02\xc9\x22\xc8t`\x88O\xd9\xfam\
+B\xdd\x0c0: \xd8\x01\xca\x87\x8e\xb6\x22>\xb2\xd1\
+\xa6s\xae\xfdB\xbc}\xd8\xd8\xc1q\x00\xf8\xe2\xece\
+\x7fN\xae?'\x00=&\xae\xd5d\x05N\x15p6\
+!w\x01\x18:\x10A\xf1\x11jB\x12i\x00\x90{\
+\x04\xd8t\x99\xf1\xdf5\x17\x85.\xf4\xe0W\x17\x00\x9c\
+\x05\xb0\xffz\xc2\xdaT\x93G\xe1\x0f5\xf4\xcfHN\
+\x19P&\xab\xbc\xa9\xc6\x01\xa0\x92\x10=\xb4A\xb8(\
+\x90\x13{m\xef\xb8\xd0\x15/\xfa\x9c|\xec\xa9\xac,\
+k\xecRB\x9e\x060r \x98\xac\xe4\x00B\x1c\x12\
+\xc5\xef5\x14T\x1eLD\xf7\xc1\xc6\xda\xafQ\xc9\xab\
+\x00\x8az9\x03\xbfQ>l\xe9J\xe4h\xf5\x14E\
+\x7f;\xe9>$\x09Z\xc2v4\xfe\xe0\xe1\xd2Ps\
+\xa2T4L\xae\xfc\xb04\xb2\xe6~K\xa9w (\
+\xe8\xf9\xd0\xe4\xf0\x81\x82\x95\xefu]\xad\xa8y\x087\
+!\x8cP\x09\x06\xe3\x1c\xfc\xbe\x1f%\x12\x8ck\xedP\
+\xf1\xaacZ\xe1'W\x1dU\x0a;\xf5\xc4Z*\xd9\
+P\x9f\xbflOw\xd7\x956U\x17(\x8d\x87I\x94\
+\x80\x9c\x00\x91\x5c\x90\x1a@\xab\x10'\xe8H\x03rr\
+\xde\x0c\x8f\x7f\xe6\xa4[?\x07\x0b\xaa\xde\x9bv\xbc\xfa\
+%\x11<\x9b\x01\xe4\xff\xd1\x88\x5c\xa4\xfd\xa2\xdb%e\
+\x91\xd5\xf9\x14\xff*\x80?\xa6\xea\xc4\xd6N)\x07\x05\
+\x80\x22$\xda\xb1\xa6<R\xf3{;v\xa9\xeapi\
+\xddyS\x9f1\xcb\xffK\xbf\x1d\x7f\x14\xe0\xd8T\x04\
+$a&K\x93\xbfj\x9e\x14\xea0\xc9o;V\xf7\
+U\xa5\xb2vQ\xf1\xa7\xec\xc18\x08\x0c\x03Qae\
+\x0d\xf9\xdb\xb4\xa3!\xa3\xf3>\x92\xbf\xec\x8ch\xbc\x91\
+\xaa\x0cI\x0c \xe4yZ\xd9\x9bL\xe2\xf2\xa6\xf5y\
+\xb6\x92\xcd\x10)\xea=\xf10U\xfc\xfe\xcd\x08\x85\x8c\
+c\x8f_\xb17^\x0dZ2\x80\xfc\xd7\x99\xebm\x0d\
+\x13\x17\xb7\x98\xf1\xbaRAJ\xf0\x06\x82\x85;\xca\x17\
+\xf8\x17\x99\xc4G\xa7\x87\x0e\x83\xf8G\x06\x90k\x13\xae\
+\xf5\x9fM\xb2\x92C+F\x8a\xc8\x93\x1e\xa8y\xa4\xac\
+!\x140\x9aL-[3N\xfd*\x1cq\xfa\xf8\x17\
+\xa3B\xdf\xe0\x07\x00\x8c\xf2\x80\x86\xb7I\x8eo6\x80\
+\xf7\xbb\x1c\x85e\xfd\x13Z\x0b:/\xc2i\xfc\x09\xc4\
+\xc7\xd7>Z\x1a\x7f7>\xa9\x16\xdf\x16G>7j\
+W\x14\x8a~\x04\xf0\xb6t\x93\x00@t\xd3C\x05\xce\
+\x89\x03&\xb1\x85\xbb\xbcrT\x8ab\x04$\x1e\xf5\x1f\
+\x08\xf8;\xda\x00\xder}\x12\xfd\xd6\x9b\x0d\x05\xcb_\
+\xedI\xef\xf5\x85\x95\xdb\x00ls\xad\x104U\xcf!\
+0\xa5_\x9b,R\xed\x0b1\xa4]\xae\xb8\xd3\xbb\xc8\
+\x9a\xb3\x8d~d\xea\x92\xf3\x00\x1b\xd3\xde\x87h\xc8'\
+&\xd9\xf4OC\xa3\x05\xe2\xd9\x13%\x22s\xdc\xb2R\
+\x11}4\xed\x01\x11\x91SFY{\xa0\x94\xa0\xf2\x8e\
+\x8d\xcc\x9bu\xa8\xd6\x5c\xe5U\xea\xb3\xb4\x07\xc4\xd2f\
+@\xe8\xa0\xd0[\xf4\xc1+\x01=\xc9\xa8O\xe4\x5c\xda\
+GY\x9a\xbe\xd3\xc6\xf9\xf3#\x0f\x8e\xe7\x9c\xcc\x07`\
+\xa8\x97\xa9\xcf:\xe7\x87t\xf4\xbc`\xa4\xfaz\xfe#\
+\xd4o\x1d,z\xee\x83.\x93\xd7\xc6\xba\xef\x0a\x9d\xd9\
+\xdd\xf8\xcb\xa9^\xd72\xbd\x8f\xb2\x02h5\x82e\xcb\
+\x08\xafW\xc6\x85j\x84\x0b\x83Z\xbf\x5cB\x97\xb9$\
+\xe7v\x8a]\xcf\x02\xf8\xa0\xeb~\xf5\xbd$\x17vc\
+\x9fS\xc0\x87\xd8\xd1K.\xda\x02\xdeW\x05d\x88Q\
+\x9dO\xda\xd2\xdc\x87\x88\x040\xbc\xc3E\xec\xf7\x1c\x0f\
+ \xdbl?}\x97\xd3\x1a\x10\x01\xe5\x92\xdd\xe6\x96\x83\
+x\xbe\x06G*\xa3\xd9\xb5\xe3\x8e\xa49C2\xad_\
+\x01B\x08\x87\xf8\xdc\xfaL\x80\x17\x14\x89\x1bo.\xa0\
+\xac4g\x08\x19s\xb3\xe9.\x93\xd7\xf7\xca\x806\xfb\
+,mv\xf8\xe9\x93\xa9kk\xa8\x99A\xaa\xc3\xf3D\
+JY_\x98}\x9a=,\xed\x01\xf1Y\xd6\xad.6\
+\xad\xd5\xfb[\xd0-.\xa3\xb9%\xc3\x10\xcaD\x17\xf1\
+Y\xaf\xf5\xd96O\x9a\xf1\xd7y\x99(\xcb\x11\xe3\xd1\
+ij\xe7\x90\xb7a\xb68\x83\x06\xc7\x8d\xd5e\x0ao\
+\xcd\x00\x224n\xde\x0e\x0c\xca;\x04 \xe6]T\x87\
+S{\xc7\xae>o\x1e\x8a\x1e\x9f\xf6\x80\x08\xcd\x15\xdd\
+\xbd\xe3*\xae@\xe4\x80\x87q\xf6>\x90]\x87\xd2\x12\
+R\x04K\xd3\x1e\x10\x12\xe5\x85\xc7C\xd9.\x80\xed\xf0\
+L\x97\xa6qgIY\xf3\xa0\xd1\x00\x8a3&K$\
+?K0\xd5\x05\xb1\xbfz\xe4@lZx\xc7\x18_\
+\x89=K\x80\x9c\xbe\xa3-z`\x00\x02\xfa\x03\xf0\x1b\
+\xd7\x11\xa2\xf6\xa7\xbb\xa9e\xbf\x07\xd1\xdc\xbb\x07\x0a*\
+\xf7\xbb\xc8\xef\xe8\x9e\xcdb\xb9\x00~e\x80\x00\x02\x80\
+\xf8\x8eI\xd48eCT,\xeb\x85\x1b\x84#F\x91\
+\x8d&\xffQ&\xa1\x80\x80\xdd\x9e\xed\xd0B\xbf\x8b\xe9\
+m\x1b0\x80\x88\xe0\xbe\x92\xb3uF\xe7\x1e\x9e\xb4\xe2\
+5\x81\xbc\xd2wk\xc5\x17\xc2\xc5\xab\xde2\xdeTS\
+`.DJ\xba\xbfy\x0e3\x97dxn\xe00\x04\
+P\xbev\xbd\xd8\xed\x82\xecI\xa3\x96\x8b\xc8\x22\x11\xb9\
+\xd8\x0b\xea}\x0e\x8d\x9f7\x14U=g\xbc$\x14R\
+\xa0<\xdeC\xb3g<\xec\xaa\xc4\xb7\x0f\x80=P\x00\
+\x01\xc0\x87\xbf\xd2\xbc\xc6\xb8\x01a/+\xe2\x0d\xc5+\
+\xd7S\x10\xa4\xe23B\xd9!W7%H\xa7\xc4O\
+@i\x01e\x1b\xa1\x9f\xd4\xa2f\x86'Wmp\xd3\
+\x1a|\xd4?\x1f\x90\xb9=\xb3\xac,7\xb2\xb8x\xd9\
+\x1e\x00\x95\xc0\x97\xcb=\x22\x22\x00\xa2\x22\xb8\xe8eN\
+u}L\x09=c(\xfcC\xb8\xb8\xf2\xfb\xbd\xb0u\
+\xaa\xe4t].cz\x88\x95\x95\xa3y>v\xa9~\
+Z\xb4\x0d\xae\x1b\xef:\xf9\x8e\xc8\xea|K\x05v\x8b\
+`R\xcfL\x1f:Z\xb3\xe3#\xcf\xb8\x1cB\x9ds\
+rQN\x8b3b\x8c\xdf\x1ed\xfb%\xda\x11\x8d\xc5\
+\xda\x01\xc4\x0en\x81]\xbe\xc0\xbf\x0b\xc07\xbc\xab<\
+$\xe1\xd0\xa7\x08\x167\x14W\xadK4\xd5g\x9c\x09\
+\x0d\x8av\xf8\xdf\x050\xa7w&B\xcf;P\xb4\xea\
+\x8f}\xd1\x19l\xaa\xddJ\xc8\xb7\xbc\x04$\xf1\xc7\xa1\
+\x89\x9a\xe9\xc7\xea\x16&RGis\xcd\xd8h\xbbo\
+ko\xc1\x00\x00\x07j\xfe\x0d\xa4*\x17\xbd\xbe\x97d\
+,\xe1\xe6hKo\x9c\x16\xa9]7\xbd\xf1\xa5\xd1^\
+w^r\xaav\xa4%\xb2\x1d\xe4\xbd}\x8b\xd0\xf1`\
+\xb0y\xcd7\xfb6{LI@\xaeE4Ok+\
+\xf6Q0R\xbdp\xc6\x99\x90gU\xd8\xc3\xe3W\xb4\
+@X\x7f\x03]\x04 \xd6\xe6\xe9\xc7\xeb\xee\xef\xf5/\
+\xb5\x8e\xa6\x96S7\xb7\x16@\xb6\x13|\x1fb}\xe4\
+\x14u\x1c9\xc8\x909b\x91y\x16\xb8\xc5\xb8\xe7\xb1\
+\xbc\xa9&\x0f\x90\x8f\x01\x8e\xe9{.\x8bvM\xbe\xae\
+\xb4\xf3\xfa\xf9\x9c\xd1\xbb\xcf\x8c\xab\xe8\xd2\xd1\xdf\xbd\xeb\
+n\xdf\xbf'\xdeWH\x89\xcf\x86\xa5*\xbc\xdc\xcd\x9f\
+\xbc79t?\x92\x8b\xa04\x0bp\x9a\x1am\xa2\xd0\
+\x01R(\xcc!0N(A\x08\xe6\x87\x8b\xaa\x8c\xb5\
+\xab\xe0\xf1\xea\xc7(\xdc\xe4\xd1\x88.\x028,\x82\x93\
+\x10}\x89\xca\x22\x94\xce\x15\x07\x93\x14Y(\xc0\xe0\xc4\
+LC\x7f\x01\xa4g\xcf\xf0\xbf\xe2g.\xdcs\xe4\xce\
+\x17\xbb\xb4\xdb3\xf6<\xe1\xef\xc8\xcd\xdfN\xf2\x1e\xa4\
+hKJ\x94\xe5\xe1pg\xfa\xf2\x86/0&\x9a\xb7\
+\xff6\xaeb\xbeE\x90\xe4g\xd7)\xe9\xd4=\xe1\x88\
+\x85\xba\x92\xe3\xa1B\x93\xbc\xbety=\xc8\xd5\x19@\
+\x92\xd7\x86\xfa$\xb0\xd6\x95G\xed\xb1_\x03\x08\xa7\xa2\
+\xb9\x22\xb8,\x05\xb7\x92\xca\xbci\xcd\xb5\x0f\x98\xa4\x0d\
+\xc1P\x8b\x80U\xa9\xf4\x12\x1a\x11\x11\xa5di\xb8\xa8\
+\xf2\xe5T\xdc\xdb\xabD\xcb\xcb3\xf6<\xd1\xe5Z\xc6\
+\xe4c\xb5\xa3D\xcb\x18\x08/\xa5\x06\x18\xd0\xb4\xac\xa5\
+\xf5\x85+_\x01\x12\xfc6\xa0DVKb\xb9\x05k\
+\x00,\xbf\xf6\xc5\xacS\xb5#\xdb\xa3\xfaY\x81\xfc \
+U\xdeU/\x22\x02\xc5%\xe1\xc2\x15\xebnvb\xe8\
+Ek\xd5\x96\xf5\xf5\xac\x98\x16[\xe11!*\x00\x19\
+\x9e:>C\x1c\x82\xcb\xc2EU\xbf\xe8\xfc}\xaa2\
+\x04\x00ri\xdb[c\x8a\xa3\xc9\xd4zC\xe9\xd55\
+\x15.\x0dwQ\x05Oe@@2\xe5\xb6\xf9\x08\xc4\
+!\xb9,\x5c\xd4\xf5\x92DJ\x03\x92r`\xb80#\
+U\xf3\x90\xd4\x05\x03\xe2(\xa8%\xdd-\xd6e\x18\x92\
+4f`I}q\xe5\xfa\x81\x96\xa9\xa7(3\xb0\xa4\
+\xa1x\xe5\xfa\x9e\x5c\xef\x03\xe4q\xe9\xc1\x9f\x83eZ\
+\x1f\x03\x0f\x9b-\xf5S\xcd{\xc8\xfe\xb7\xfd\x07:\xcc\
+\xccF\x8ay\xc7t\x00\x00\x00\x00IEND\xaeB\
+`\x82\
+\x00\x00\x02\xed\
+<\
+svg width=\x2248\x22 h\
+eight=\x2248\x22 viewB\
+ox=\x220 0 48 48\x22 f\
+ill=\x22none\x22 xmlns\
+=\x22http://www.w3.\
+org/2000/svg\x22>\x0a<\
+path d=\x22M24 40C1\
+9.5667 40 15.791\
+7 38.4417 12.675\
+ 35.325C9.55833 \
+32.2083 8 28.433\
+3 8 24C8 19.5667\
+ 9.55833 15.7917\
+ 12.675 12.675C1\
+5.7917 9.55833 1\
+9.5667 8 24 8C26\
+.8333 8 29.3167 \
+8.575 31.45 9.72\
+5C33.5833 10.875\
+ 35.4333 12.45 3\
+7 14.45V8H40V20.\
+7H27.3V17.7H35.7\
+C34.4333 15.7 32\
+.8167 14.0833 30\
+.85 12.85C28.883\
+3 11.6167 26.6 1\
+1 24 11C20.3667 \
+11 17.2917 12.25\
+83 14.775 14.775\
+C12.2583 17.2917\
+ 11 20.3667 11 2\
+4C11 27.6333 12.\
+2583 30.7083 14.\
+775 33.225C17.29\
+17 35.7417 20.36\
+67 37 24 37C26.7\
+667 37 29.3 36.2\
+083 31.6 34.625C\
+33.9 33.0417 35.\
+5 30.95 36.4 28.\
+35H39.5C38.5333 \
+31.85 36.6167 34\
+.6667 33.75 36.8\
+C30.8833 38.9333\
+ 27.6333 40 24 4\
+0Z\x22 fill=\x22#66708\
+5\x22/>\x0a</svg>\x0a\
+\x00\x00\x01\xb3\
+<\
+svg width=\x2248\x22 h\
+eight=\x2248\x22 viewB\
+ox=\x220 0 48 48\x22 f\
+ill=\x22none\x22 xmlns\
+=\x22http://www.w3.\
+org/2000/svg\x22>\x0a<\
+path d=\x22M13.05 4\
+2C12.225 42 11.5\
+187 41.7062 10.9\
+313 41.1188C10.3\
+438 40.5312 10.0\
+5 39.825 10.05 3\
+9V10.5H8V7.5H17.\
+4V6H30.6V7.5H40V\
+10.5H37.95V39C37\
+.95 39.8 37.65 4\
+0.5 37.05 41.1C3\
+6.45 41.7 35.75 \
+42 34.95 42H13.0\
+5ZM34.95 10.5H13\
+.05V39H34.95V10.\
+5ZM18.35 34.7H21\
+.35V14.75H18.35V\
+34.7ZM26.65 34.7\
+H29.65V14.75H26.\
+65V34.7Z\x22 fill=\x22\
+#667085\x22/>\x0a</svg\
+>\x0a\
+\x00\x00\x01\xf7\
+<\
+svg width=\x2248\x22 h\
+eight=\x2248\x22 viewB\
+ox=\x220 0 48 48\x22 f\
+ill=\x22none\x22 xmlns\
+=\x22http://www.w3.\
+org/2000/svg\x22>\x0a<\
+path d=\x22M9 39H11\
+.2L33.35 16.85L3\
+1.15 14.65L9 36.\
+8V39ZM39.7 14.7L\
+33.3 8.29998L35.\
+4 6.19998C35.966\
+7 5.63331 36.666\
+7 5.34998 37.5 5\
+.34998C38.3333 5\
+.34998 39.0333 5\
+.63331 39.6 6.19\
+998L41.8 8.39998\
+C42.3667 8.96664\
+ 42.65 9.66664 4\
+2.65 10.5C42.65 \
+11.3333 42.3667 \
+12.0333 41.8 12.\
+6L39.7 14.7ZM37.\
+6 16.8L12.4 42H6\
+V35.6L31.2 10.4L\
+37.6 16.8ZM32.25\
+ 15.75L31.15 14.\
+65L33.35 16.85L3\
+2.25 15.75Z\x22 fil\
+l=\x22#667085\x22/>\x0a</\
+svg>\x0a\x0a\
+\x00\x00\x1a\x93\
+\x89\
+PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
+\x00\x00J\x00\x00\x00H\x08\x06\x00\x00\x00Q\x18cz\
+\x00\x00\x01\x85iCCPICC prof\
+ile\x00\x00(\x91}\x91=H\xc3@\x18\x86\xdf\
+\xa6\x8a\x22-\x0ev\x10q\xc8P\x1d\xa4\x05Q\x11G\
+\xadB\x11*\x84Z\xa1U\x07\x93K\xff\xa0IC\x92\
+\xe2\xe2(\xb8\x16\x1c\xfcY\xac:\xb88\xeb\xea\xe0*\
+\x08\x82? \xae.N\x8a.R\xe2wI\xa1E\x8c\
+w\x1c\xf7\xf0\xde\xf7\xbe\xdc}\x07\x08\x8d\x0a\xd3\xac\xae\
+q@\xd3m3\x9dL\x88\xd9\xdc\xaa\xd8\xf3\x8a\x10\xc2\
+4\xc7\x10\x93\x99e\xccIR\x0a\xbe\xe3\xeb\x1e\x01\xbe\
+\xdf\xc5y\x96\x7f\xdd\x9f#\xac\xe6-\x06\x04D\xe2Y\
+f\x986\xf1\x06\xf1\xf4\xa6mp\xde'\x8e\xb0\x92\xac\
+\x12\x9f\x13\xc7L\xba \xf1#\xd7\x15\x8f\xdf8\x17]\
+\x16xf\xc4\xcc\xa4\xe7\x89#\xc4b\xb1\x83\x95\x0ef\
+%S#\x9e\x22\x8e\xaa\x9aN\xf9B\xd6c\x95\xf3\x16\
+g\xadRc\xad{\xf2\x17\x86\xf2\xfa\xca2\xd7i\x0d\
+#\x89E,A\x82\x08\x055\x94Q\x81\x8d8\xed:\
+)\x16\xd2t\x9e\xf0\xf1\x0f\xb9~\x89\x5c\x0a\xb9\xca`\
+\xe4X@\x15\x1ad\xd7\x0f\xfe\x07\xbf{k\x15&'\
+\xbc\xa4P\x02\xe8~q\x9c\x8f\x11\xa0g\x17h\xd6\x1d\
+\xe7\xfb\xd8q\x9a'@\xf0\x19\xb8\xd2\xdb\xfej\x03\x98\
+\xf9$\xbd\xde\xd6\xa2G@\xff6pq\xdd\xd6\x94=\
+\xe0r\x07\x18|2dSv\xa5 -\xa1P\x00\xde\
+\xcf\xe8\x9br\xc0\xc0-\xd0\xb7\xe6\xf5\xadu\x8e\xd3\x07\
+ C\xbdJ\xdd\x00\x07\x87\xc0h\x91\xb2\xd7}\xde\xdd\
+\xdb\xd9\xb7\x7fkZ\xfd\xfb\x01\xa7vr\xbc\xf6x\x14\
+.\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\
+\xbd\xa7\x93\x00\x00\x00\x09pHYs\x00\x00.#\x00\
+\x00.#\x01x\xa5?v\x00\x00\x00\x07tIME\
+\x07\xe7\x0c\x0d\x0d+\x19\xea\xccr\xbc\x00\x00\x00\x19t\
+EXtComment\x00Creat\
+ed with GIMPW\x81\x0e\x17\
+\x00\x00\x18jIDATx\xda\xed[y`T\xd5\
+\xd5\xff\xdd\xfb\xde\xac\x99}\xb2/da\x93E@P\
+\xb1\xdaZZ\xfd\x94-\xc8\xe2\x04\x14\xb7JK\xe5\xf3\
+\x0b&\x80\x88\xd5\xb6Q\x8b~\x22\x90\x80b-\xa2b\
+QI\x18\x11H \x91\x8a\x15k\xddQPY\x0ck\
+\x12\xb2'\x93\x99\xc9\xccd\x96\xf7\xde\xbd\xdf\x1f\x81@\
+\x08\x89Qh\xc1\xaf\x9c?g\xee\xbcw\xde\xef\x9d\xfb\
+;\xe7\xfc\xce\x1d\xe0\x92]\xb2Kv\xc9.Y\xaf\xcd\
+\x91\xb7O\xedpl\x10.6\xbf\xc8\xc5\xe2\xc8\xf4\xe7\
+\xf8H@~\x82s\x0c\x03\xe1!\x0a\xfa1\x04\xe9\x0f\
+\x85st\x15\x97\x80\x02\xe0X\xe2\x8f\xa7:\xed\xef\x08\
+\xf8o8\xa0=\xe3\xebVp\xaca\xb2\xb8\xd89\x8f\
+\xb4\xfcG\x02\xe5\xd8\xc0\x05\xda \xfd\x0f\x07\xfd=\x01\
+\xb7\xf7\xec%\xa9\x00g\x8f\x17e\xab_\xf9\x8f\x02j\
+\xc6Jy\x02'\xfc\x7f\x01\x0c\xfd>\xbf\xe3\xe0\xdf\x10\
+\x8aYE\xf7\xab?\xff\x7f\x0d\xd4\xa4\xa7\x03\x89:\xbd\
+\xfae\x007\x9f\xcbu8\xe7\xaf\x8bT\xf5\xc8\x1b\xff\
+C*\xff_\x015\xe9in\xd4F\xc9\x8f\x10\x86\x5c\
+\x10\xa8\xbb[\xa7\x84$\xb8\x0f\xd6B\xd0\xa9aJ\x8d\
+\x86\xa0V\xf5t\xd9V\x0e\xb6\xbc\xd1\xa5^\xbc3\x8f\
+\xc8?z\xa0\xb2V\x86o#Dx\x0e\xe0\xb6n#\
+Da\xf0V6\xc1_\xed\x02g\x1c\x00@U\x14\xa6\
+\x94\x18\x18R\xec \x84\xf4\xc8_DQ\xe6\x15>\xa0\
+\xd9\xf4\xa3\x04\xea\xd6\x95\xd2\xcfD\x82\xa5\x1c\xb8\xba\x87\
+-\x84`S+\xdc\x07\xeb\xc0d\xe5\xackD\xad\x1a\
+\x96~q\xd0E\x9b\xbekC~\xaa\x12Tw\xbf\xf6\
+\xdf\xa4\xfcG\x01\xd4\xac5\xdc\xe6o\x93\x0a@\xc8\x9d\
+=\xad\x0b{\xdb\xe0>T\x0f\xc9\x1f\xec\xd5u5\x96\
+(\xd8.K\x84\xa8U\x7f\x17\xe1\xbf\xc0\x15\xd5S\xce\
+\x1cRuQ\x025s%7)T\x9e\xc3\x81\x05\xe0\
+\x88\xee\x96\x87\x22\x12Z+\x9b\xe1\xaf\xf9ae\x911\
+\xd9\x0eC\xb2\x1d\xa2\xb6g\xfe\x22\x9c,PY\x847\
+\xd6\xddE\x02\x17\x0dP\x8e\x15\xd2X\x22`\x09\xe1\xb8\
+\xbc\xdb7-3\xf8j\x5ch\xadl\xea\xe0\xa1\x1fj\
+\x82F\x05sZ,\xa2\xe2-=?\x01A9!\xe4\
+\xd1\xc2\xfb\xc57/8P\xb3\xd7*s\xbd>\xb6\xa2\
+\xa75A\x97\x0f\xde\xa3\x8d\x90\x02\xa1\xf3\xba\xcdU\x06\
+-,\x19q\xd0\xda\x0c=p=`\x8c\xa2\x8b^\xfc\
+\x95\xf0\xf4\xb9\xdc\x8b\x9e\xab\xb3\xdc\xef\xbf\x22%^\x80\
+^wv\xcc\x99\xac\xa0\xad\xa9\xf5\xbc\x83\x04\x00\x92?\
+\x04_\x8d\x0b\xbc\x9bD\xa0\xd3\x12$\xc4P\xc0\xe7\x1b\
+p\xae\xf7\x12\xcf\xf5\x02\xfb\xfe~\x14q\xe96)\xed\
+\x8ax\xc1\xa0\x17\xa9\xdb\xc3 \x9d\xe67\x15\x05\xd8/\
+K\x82!\xc1\x0a\xcf\x91zDZ\x83\xe7'\x9a\xa2\xb4\
+\xb0\xf4\x8b\x87\xd6\x1a\xd5\xf5\xa1\x04\xc0f\xa6\xa0R\x84\
+\x1d\xfe\xb0\x96\xb9\xaa[\xc9\x05\x07\x0a h\xa8p\xab\
+\x9a\xab[\xa5\xb8t\x0bRG$ (\x11\xea\xf2\xb0\
+\xceY\xcb\xacG\xdc\xc8\x0c\x04\xea=\xf0\x1ci\x00\x93\
+~X\x8dHE\x0aC\xb2\x1d\xa6\x94h\x10\x81v\xe1\
+\x11\xb3\x91\xc2\xa8\xe7J\xcd\xfe&\xd4|\xdbDeI\
+\x11A\x08\xbf\xe0@q\xc2\x09\x01\x81\x22+\xaa\xdaC\
+.\xb8j[\x91:4NJ\xedkS\xb5x\x19|\
+\x81\xce>F\xc5[\xa0\xb5F\xc1_\xebFke\xd3\
+\xf7\xcbxIv\x18S\xa3!\xa8\xbb\xba\x1d\xa5#\xb0\
+[(\x9a\x8e\xb5(\x07\xf6\xd4\x139$\xd3\x19\xd7\xd7\
+`o\xa5\x11{+M\xb8\x08\x22\xea\x8c\xfa( \xe1\
+\xe0\xa7\xd5\xaa\xe3\x07\x9a\xe4!?M\x859^+6\
+63Dd\xde9k\xa5\xc7\x22*\xc1\x0a\xf7\xc1:\
+\x84Z|=^Sm\xd2\xc1\xda/\x01j\x93\xae\xeb\
+\x03\x88@\x9cM\x80\x1c\x0c\xc9{\xdf\xad\x15\xbc\x8d~\
+\xe1Z\xa1\x12aQ\xc4\xc8\x0c/v~c\x07\x01\xf8\
+E\x00\x14\xc5\xd9\xfc\x08\xb6\x86\xc5]\xa5\x07\x11\x93j\
+\x8e\xf4\x1b\x95\xa8\x0eC\x84\xdb\xcbp:\xef\x8aZ\x15\
+b\x86\xf5A\xd8\xdb\x86\x96\xf2Z\xc8m\xe1\xceWV\
+\x09\xb0\x0dL\x82\xd6n\xe8\xd2\xc6\x10\x02D[)t\
+*\x8e\xf2\x8f\xab\xe4\xe6*\x8fh\x89mC\xd2\xc00\
+\xa6T\xee\xc7\xb2\xc8O\xb1pmR\xfbZ\x8e\x8b/\
+\xa2\xce\xb4\xa6J\xaf\xba\xb9\xaa\x15\xa9\xc3b\x918(\
+\x16\xfe \xe0\xf6v\xe5\xaf\x84\xab\xfa\xc1_\xe3\x82\xb7\
+\xaa\x19\x5cV`H\xb1\xc3\x9c\x1a\x03Bi\xd7t\xaf\
+\xa7\xb0Y\x08\xea\xca]\xf8\xfa\xabz\xc8\x92\x22\x02\xc0\
+\xe8[\x8e\xe2\xc0G\x09x04\xaes\xe6\x05\xbb\xf8\
+\x81:\xd9\xd3U|\xd5\x80\xfa\xc3nd\x8cL@j\
+\x8a\x19\xcdn\x86@\x90wbbC\xb2\x1d\xfa8\x0b\
+\x00\x0e\xaa\xea\xea\x9aVC\x10k\xa3\x084\x07\xf0\xd9\
+\xa6*\x84\xdb\xa4N\xdf\xbf\xf3\xca`0\x99\x9e\xa5\x96\
+\x12\xd8\x85\x07J\x11\x9f\xe7\x824\x86\x00\xa9\xdf\xb54\
+\x14\x88`\xff\x07\x95\x88\xb2j1\xf8\xfa4XLj\
+4\xba\x14\x9c\x9e\x00\xa9J8k\xba\xb7[(\x88,\
+\xe1\xdb\x0f\xea\xe0\xaa\xf6vS\xb3\x9d\xb5,t\x83\x93\
+\xd7.\x8a\x16\xe6g\xd9\x87b\xd4\x11\xfaN\xc8\x13\x18\
+\xce\xe5\xde\xbf\xbc\xf8~v\xa4\x0f\x8bE\x88\x8b\xf0\xb6\
+v\xae\xbf\x00\x80\x12\xc0j\xa6\xd0\xab9\xaa\xbei@\
+Mys\xaf\xdb\x1f\x22P\xe8l\x86]A\x8f\xe7\x96\
+\x0f\x0bG\xd6^P\xa0&M\xcd\xb9\x8ed\xdc`\xd3\
+\xa5\x8e]\xc6\x81\xferH\x82\xf7h\x03\xda\x1a\xbd\xbd\
+w\x80\x12\xf4\xbd2\x11q\x196x\xfd\x1c\xa1\x08\xc0\
+\x19\xa0\xd5\x00V\x13Ec\x85\x1b\xc7\xf6\xd4A\x0a\xf6\
+\xbe\xee\xd2Z\xa3`\x1d\x98t\xa2q\xe6\xfb\xa5\xa3\xa5\
+\x8fJ\xc7w\x86J\xde,(\xfb\xc1\xfd\xe5\xb9\x005\
+h\xf4X_\xca\x15\xe3WP\x8dqTDj\xaf\xc2\
+\xf51&hLzH\xbe\x10\x98\xa4\xf4\x82\xc0\x80\x96\
+\x1a\x1f\x9a+\xbd0[T\xb0YE\x18t\x80\xec\x0f\
+\xe2\xdb\x0f\xabPw\xd0\x05v\x22J\x09%P\xa9E\
+0\x85u_F\xf4O\x809#\x0eTl\x7f4\x83\
+\x9e\xc6\x98-\xa6\x18_\xf9\xae%\xe5\xe5\x1fF.\xd8\
+\xd6\xbb\xf9\xd7\xdf\xbe\x9c:<a\xa6!\xce\xa8nv\
+3\x84#\xbc\x03\x80@\x83\x1b\xde\x8a&(!\xa9\xf7\
+\x0e\x11\x02\x10t\xd9b\xa2\x8a\x22q`4\x98\xc2P\
+}\xa0\xf9\x8cj]\x801\xc5\x0ec\xb2\xbd\xa3Z\xd7\
+\xaa\x09l\x16\x8a\xb0'\xc0\x8f|Q\xf7\xca\xf6\xd5\x03\
+f\xfd[\x9a\xe2\x89Y\xb9\x99\x9c\xf3.\xc0\xfaZB\
+d\xef\xcec\xea\x8a]\xc7\x11mP\x10k\xa3\x10\x84\
+\xf6W\x10\x15oE\xdc\x88t\x18\x12\xad=\xcb\xb9g\
+d\xc83A\x8a\xefgCB\x7f\x1b\xd4:\xb1\xcb\xab\
+5&\xd9\x10\x7fu_\x98Rc@\x04\x0a\x81\x021\
+V\x0a\xabNa\x15_TG\xbe\xdaq\x94\xfb\x5cm\
+g\xa7\x8e\xdbr\xc7\x8e\x1a5[u~\xb3\x1e'\xfa\
+\xcc\xacy\xaf\xdd2#\xf7\x85-\x85\xf9\x1f\x9c\xf6`\
+\x84\x10\x82\xfa#n4W\xb5\xca\xf1\xfd\xedH\x19\x1c\
+#\xb6I\x14\xeeV\x06A\xab\x82u@\x22\x8c)v\
+\xb8\x0f7 \xe4\xf2\xf5\xfa\x96\xf6d#\x0cV\x1d\xa8\
+H\xc1\x15\x8eHPF\x9b'\xdc\xa1x\x9a\xd3c\xa1\
+1\xeb;\xd6[M\x14\xe6(\xa0j_#\xaf\xf9\xb6\
+\x99&\x98\xfcj\xa3VDk[g,&e\xcd\xbf\
+^\x01\xbf\x1b2w~\xf1\xc5j\xe9\xbcr\xd4\xc1\xfd\
+\x1f\xef\x1b\xf3\xd3a%\x9e\x88\xe6\xa6\x81\x83G\xcfM\
+\xed{\xe5gG\xcb?\xf3\xa5\x0c\xb9o2\x01\x19\x01\
+\x00\x8cq\xda\xda\x14\xa0\xcdU^\x18\xcdj%!Y\
+G\x15\x05\x88H\x00U\x89\x88\x8a3C\x15\xa5\x85\xe4\
+\x0bv\xf0NO\x16\xdb\xc7\x02A-\x00\x1c\x08\xfa\x22\
+\xa8-w!\x12a\xb0\x0dJ\x82%=\xb6C\x16\xd6\
+k\x09\xe2\xec\x02B\xcd\xbe\xc8\xde\x7fT\xb0`u\xb3\
+@\xa1 \xff\xb7\xfbP^mD\x83[\xfb\xe5\xf1\x03\
+/\x14\xdf2c^J\xff!\xa3W3\x10\xbdMC\
+\xff\xe0\x5c\xbf\xfc\xc0\x0f\xe6\xa8\x89\x8e\xdc$\x86\xb0R\
+\xea|\xbe\xbe\xbb\x1f\x8d\x9d\x91\x93&*4\x1bP\x94\
+\x16vW\x02 \xdcq\xd6&6Z\x8b\x81\xa3\x93!\
+\x1a\xf4\x9d\xea%\xce\x18\x02u\x1ex\x8e5v\xab%\
+\x01@\x9f\xa1\xb1\xa0\x94\xa0j\x7f\xbb*jL\xb6\xc1\
+\xd4'\x16Dlg\x0c\x81\x02q\xd1\x02 EP\xfe\
+Q5<\x0d~P\x91\xe1\x09\xe1\x1d\xbc-\x0fDs\
+\xa2\x1d\x07\x8e\x1bA\x10*\xb2\x08\xeb?\x07H\x7fY\
+\xc6c\xdb\xdf*\xa8\xeb\xee\x9e\x93'\xe7X\x14\x9d \
+\x96\xac_\xd6\xdc#Pcg\xe4\xa4\x89\x8c\xe4\x82\x12\
+\xf7\xd6\xf5\xcb\x1e\xebI\xa2\x980}\xc1P\x9f\xf2_\
+\xaf\xcb<nX\x8f\xf5R\x7f;\xd2\x87\xc7#\xa4P\
+\xb4x\x19\xd8\x89`\x92\xc32|UM\xdd\xea\xe7\xa2\
+F\x80\x1cV\xa0\x8b5\xc3\xd6/\x1e\xf4\x84j@\x08\
+`5R\x18u\x1c\x87v\xd5\xa0\xf1\xa8\x1b\x9c\x03\xa3\
+\xc6\x1d\x83\x1c\x11 \xbc'\xe2+\x16\x0f\xe5\x04\x05\xab\
+H\xcd\xa1(\xf1ow\x96\x16\xae\xf8\xb4;\x1f\x07;\
+\x1c\xea\x0c\xd2'\x87\x83\xa5\x12\xae<\xbd\xd5\xb9\xb2\xaa\
+\x0bP\xe3o\x9f\x9f\x0a\xa6\x5c_ZX\xb0\xeeTd\
+-\xbc\x1a\x90\x9e\x86\xa0\xfcik\xe1\xcaw;\xed\xf1\
+I\x0b\x8d\x8aV\x9aJ\xb4\xd8\xe1\x0a\xdc\xb3\x98\x80\xdf\
+\xfd\x9dd\xa8\x11\x91<(\x06\xc9\x83b\xe0\xf5sx\
+|\xa7\xb6\x9e\x1c\x8c\xa0yo\x15\xa4@\xb8\x8b8g\
+\xed\x1f\x0f\x8d\xe5\x948g\xd4S\xd8\xad\x04\xf5\x87[\
+plw\x1d\xe4\xc8\xa9\x88\xec{E#*\xf7FC\
+\x96\xe8\x19\x09\x82\xbd\xa4a\x7f]`R\xd3\x89\x08y\
+\xb6\x14\x17\xbf\xdc\x89('d\xe5\xdeH\x81\xf9\x0cX\
+\xb6mC\xfe\x8eS\x810\x7f\xaa:\x12z\x7f\xd3\xa6\
+U\xae\x8e\x88\x1a\x9f\x95\x9bE9\xee\x0c3e\xde;\
+\x1bW\x1e:\xf9yfVn.\x07\x1f!F\xf0\xc7\
+\xcd\x9b\x0b*&N\x9f?\x93@\x99*+\xe4/T\
+\xe0\xd3\x82\xca\xa8)!~yL\xaf\x8bA\x83\x1a\xe9\
+#\x13`O6\xa3\xc1\xc5\x10\x0a\x9f\x0a\xd8\x90\xcb\x87\
+@\x83\x17 \x04\xfaX\x13tv\xe3\xa9\xc6Y\x0d\xc4\
+X\x05\xb4\xb5\xf8q\xf0\xd3\x1a\x04[\xc3\xbd/@\xc9\
+\xber=\xdd\xb5\x9b\x82?\xc79\x99\xc7)/\xdbZ\
+T\xb0f\x92#\xb7\x1f'\xe4I\x0e\xfew\x1d\xaf~\
+\xd9\xe9tF\x00`\xe2\xf4\x9c\x91\x9c\x93\xc7\x05NV\
+\x17;\x97\x17w\xd9zc\xc6\xe4i\x8d\xd1\xde<F\
+\x01U\x84\xbf\xb0ysA\x05\x00\xdc4eA\xacF\
+%?\xcaA\x86\x80\xe3\xb9\xad\xce\xfcMc\xc6\xe4\x89\
+;w\xe6\xc9\xe3\xb2r\xae\x8b\xb0Q\xeb\xc2HOg\
+\xdc\xd0k\xe7\xcd1Q\x18\xf4\xb3>P\x04\x15\x5c\xee\
+\xce\xfd^\xa7lC\xdb\xfb<\x15\x14\x1c\xdb]\x87\x86\
+c\xee\xefQ\x93\x85\xa1Ay\xb9\x06_\xe6\x96\xbdY\
+P\x06p\x02\x10\x9e9}\xc1-\xe0\xca\x03\x9c\xa0B\
+\x11\xcd9e\xaf\xe7\xb5\x02\x80\xc3\x91\x1d\xd3F\xc4\xf9\
+\x04\x90h\xc8\xbb\xe4\xf4\xc8#\xdd\x91\xb5\xc00\x8fp\
+ZQw\xd4\xff\xec\xe9)t\xf2\xe4\x1c\x8b\xac\xa1\x0f\
+\x83\xf3\x0cF\xe8\xf6\xd2\xc2\xa5/e=\x17\x99\xc9\x82\
+\x81\xb5\xad\xb5~\xc1_\xe3\x06\xbe\xc78*iP4\
+\xfa\x0c\x8aAP\x11\xe1\xf6\x9d\xe2/B\x00\x8b\x91\xc2\
+\xa8\xe5\xa8\xda\xd7\x88\x9a\xf2\xe6^e\xca\x93\x15|T\
+\x82\x0d\x86D\xbdG\xd4\xeb\xe7\x1fy\xf5\x81uI\xe9\
+Q\xd3\x18\xe5\xb7\x81\xd2}\xac5\xb4\xb2\xb4\xf4T\xb2\
+\x1a\x959[\x9f\xa0\x8f\x9a\x0b\xf0\xc1r\x18\x7fx\xfb\
+D\x80\xf4\xba2\x9f8c\xee\x0dP\xc4\x1c\x85\xf0\xd5\
+\xa3\x07\x9b\xb7}\xbe\xdf{'%\xb8\x81\x81?\xb5\xad\
+\xa8\xe0@fV\xcem\xa6A\x93\x17(1\xd7\x8d\xe4\
+'\xb0\x91C\x11\xb8\x0f\xd5!\xe4\xf2\xf7^\xc2P\x0b\
+\xe8;*\x11\xf1}m\x08\x8698\x07t\x1a\xc0]\
+\xef\xc7\xc1\x8f\xab\x11n\xeb}\xe7\xa1\xb1\xe8a\x1b\x90\
+\x08Q\xaf\xe9h\xac\xa9\xeb\xa3\xc3\xad\x07\x8a\x1f/)\
+\x5c\xb6n\xe2\xb4\x07\xfbC\x94\xf38\xc7\xdf\xb6m\xc8\
+\x7fuR\xd6\xbc_0\xce\xfe\x9b\x13a\xcd\xb6\x0d\xcb\
+\xb6\xff\xe0\x16&//\x8f\xee\xda\xef\x99\x03\x90\xe1\xe0\
+\xec\xdd\xad\xce\x15E\x9d\xe6z\xcfz^\xb6$\x9a~\
+\xe5\xf20\xf8\xdbNER[c+\xbcG\x1b \x87\
+z\xff\x90z\x93\x16\xd6D#\x08\x01\xbcM\x01\xf8\x9a\
+\xdb\xce\xd8\xaez\x08j\x01-5]\x8bV\x95N\x03\
+Sz\x0c\xf4\xb1\xe6S\xd7\xd3\x12\xd8\xcc\x94\xb5\xd6\xb5\
+\xbe\xf2\xe2\x03\x96_\x9f\xbe~|Vn\x16!\xb8\x9d\
+3|X\xea\xcc\x7f\xe6_\xde\xeb];u\xf7+\xe6\
+X\xc3\xed\xfd\xaeNV\x04\xbdF\xe7\xf2(\x88H'\
+\xeb%\x0e\x7fm\x0b|\xc7\x9b\xa1\x84\x7f\xf8\xc9\x9c(\
+\x8b\x16\x1a\xbd\x0a\xb6$#B\xfeH\xa7^\x8f\x88\x14\
+\xa6d;\x8c\xa91\x1dm\x92Z\x0d\xd8\xcd\x14\x92\xaf\
+\x8d\x1f\xdeU\xc7}M\x81W?z\xeb\x8a{/\xf8\
+\xb8\xca\xdb\x14P\x7fQz\x90\xc5gXy\xea\xb0X\
+\x22\x11\x15<^\x06\x09\x04\xc6d;t\xd1&\xb4V\
+4\xa2\xad\xc1\x0b\xce{\xcf_*\xad\x08\xbdI\x03[\
+\x92\x11L\xe1\xa7\x9a\xe6\x93\x11\x13k\x86)=\x16*\
+\x9d\xba\x83\xd7lf\x0a\xad\xa0\xb0\xea\xbd\xf5\xbc\xf6p\
+\x0ba\x92B/\xaaq\x158\xa7\xf5GZ\xe0\xae\xf5\
+)\x89\x03\xa3\x95\xf8\x016Uk\x1b!\xbe\x00\x87\xa8\
+U\xc1vY\x12\x0c\xc96x\x0e7 \xec\xe9\xdd\xb9\
+\x89\xc4~6\x08j\x01\x9cs\x10\x00\x8d\x15\x1e\xb4y\
+\xc3P\x1bu\xb0\xf4\x8d\x87\xc6r\xaa\xcf3\x1b\x08\xac\
+f\x8a\xe3{\x1bP}\xa0\x99\xca\x11\x05\xf7\xdcp\x1c\
+\xbb\x8e\x98\xb1\xb7\xe2b\x1cW\x05%\xe1\xd8\x9e:\xa1\
+\xee\xb0\x0b}\xafLTR\x12LB\xb3\x87\xa1-\xc8\
+\xa16\xe8\x10;\x22\x0dm\xf5\x1ex*\x1a\xbfS~\
+\xe1'\xc8\xc1u\xdc\x87\x90/\x0cY\xe1\xed\x873\x12\
+\xac \x94\x9c\xd0\xd1\x81h\x8b\x80\xa0\xbbM\xda\xb3\xbd\
+\x96\xf8\x9a\xdb\xc4\x1b\x85ChS\xa9\xd17>\x88\xd2\
+/b/\xeeqU\xc8\x1f\xc1\xbe\x9d\x15\x82%\xde\xc8\
+/\xbb&\x89\x98\xa2Tp\xb9\xdb\xe5^}\xbc\x05Z\
+\xbb\x11m\x0d^x\x8e\xd6w+\xef\x06\xbda\x04\x03\
+\x11\x04\xdc!\x18R\xec\x88I\x89\xee\xd0\xd4E\xa1}\
+\x5c%0\x19\xe5\x1fT\xa1\xa5\xd6\xa7\x8a\xe9\xe3\x83u\
+h\x04\xd7\x1d\xaa\xc4\x9f\xa5k\xf0\xfb\xd7\x0d?\x9eq\
+\x95\xa7\xdeG>\xd9\xfc-\x92\x07\xc5 mh\x0c\x02\
+\x92\x00\x97\x97\x81\xaa\x04\x18\x92m\xd0F\x1b\xe1=\xd6\
+\x80\xb6\x86\xae\xf2q\xd3q/\xb46\x03\xe2\xae\xea\x0b\
+\xd5\x89tO\x08`6PX\x8d@\xf5\xfe&T\xed\
+k\x84,\xb5\xd7W#n\xa8\xc2W\xef\xa5\xe0\xb1\xf0\
+\x8d?\xceq\x15\x00T\x1fhBs\x95\x07)\x83b\
+\xd0g@4\xbc\xbe\xf6~O\xd4\xaa`\x1f\x94\xdc\xae\
+W\x1d\xac\xeb8\xc4!\xea\xd5\xb0\xf6K\xe8t\xa4\xc7\
+\xa8oW-=5^|\xb2\xa3\x06\x913t\xf4\x1d\
+k\x07\xe3,\xda\x22\x08\xa8r\xee)\xeb\x1c\xed\xea)\
+{\x06\x08\x14\xdb\x09\x90\xf6}\xfa\xbdacR\xa16\
+\xe9P\xdb\xd8\xb9}\xe1\x0a\x03g\xbc\xd3\xd8J%\x02\
+16\x01,\x10\xc4\xa1\xcfk\xe1i\xec\xfd!:\x0e\
+\xd4sQ\xf8\xe5'E\x97\x1f\xb8\xa0\x11uE\xe6p\
+\x11L6\xc7\x86\x9b\xb0\xe7\xcb\x16\xb8<\xdf]/\x85\
+\xfc\x11|\xb6\xf5\x10\xe2\xd2\xcc\xc8\x18\x99\x88\xc8\x89q\
+\xbb\xa4\xb4\x8f\x99\xc8\x09\x8c(m\xaf\x87\xf4*\x86\xa3\
+\xbb\xabQ{\xa8\xf7\xc7\x19\x0dz\x8a\xd1\xd7\xc6\xc2\xad\
+\xb1\xb5IP\x91O\x8a.\xf0\xd6k\x0bJ\xf3\xef\xbf\
+Y\xb4\x8e\xee\x9b\x00\x97'\x1a\xafnl\xc0\xc62W\
+\xaf~\xdbP\xe1EC\x85\x17\x19#\xe2\x918\xc0\x0e\
+\x7f\x84\xb6O\x8fy{6\xb3\x18\x08j\xcb\x9b\xf0\xf5\
+7M\x90\xa5\xde\xed\x1eB\x80\x9f\x8f6c\xd1\x9c\x14\
+\x18\xa3\x04\xbc\xb7\x9fe\xbc\xf4\xbe\x94\x0d`\xce\xbfe\
+\xb80e\xca\x82\xd8\xb3}\xae3\xa8\x1e[\xf5\x8e\xb2\
+\xf1/\x7fW\xc0\x04\x15\xe6\xcdJ\xc6k\xf9\x031\xa4\
+\x7fT\xaf\x9d8\xba\xa7\x1e\xbbJ\x0e\x22\xd4\xe4\x81U\
+-\xc1\x1e\xa5\x00~?v\x95\x94\xe3\xc8\x97\xf5\x1d \
+\x09\x22\x85F\xd7\xfd,`\xf8 =\xfe\xfcD?,\
+^\x90\x06\x85\x08X\xfb\x0f\x86Wv\xb2\xf7\x94\xb0\xea\
+\xac\xc7\x12\xc7M\xcf\xee{\xde8j\xd2\x1d\x0f\x0cT\
+$\xb2\x88pb\xe4\xe0\xc7\xb9,\x15\x94\xbe\xb5\xaa\xe3\
+\xaf\x15\x99\xd3\xe7]\xc3\x81\x1c\xebe\xe3+Y\xdc\xf5\
+YY\xa3\x85\xb4_\x0e\xa60h\x81\xb7\xdfwcM\
+Q=\xea\x1a#\xe7\x9c\x0cD\x95\x80\xb4\x11q]Z\
+\x18\x00\x88\xb6\x8a\xf8\xf5\x8cxd\xde`GH\x02J\
+\xf70\xbc\xb3W)\x8f4~\xb3\xb1\xf1\xcb\xd7b\x14\
+N\x0b\xca\x9c\xcb\xf6\x9f\x5c\xefp\xe4\xdaB\x94\xcfa\
+\x0c\xfd\x08A\x1d\xe1\xf2\x8b%\xce\xe7\x8e\xfd \xa0&\
+O\xce\xb1DT\xf4^B\x90f\x12\xb4\x8b\xd7\xaf\x7f\
+\xb2a\xe2\xed\x0f\xf6\xe7\xb24\x1f\xa0\x9f\xe99\xdf\x1e\
+$\xec>\x80\xc8V\x8d\xb8t\xdd\xba\xa5\x81)3\x1f\
+\x9c\xa8\xc9\xb8%;~\xe05\x03\xa6]I\xd2~:\
+\x90\xa2-\xc8\xb0nS\x03\xde\xda\xee\x82?\xf0\xfd\x93\
+\x0f\xa1\x04\x89\xfd\xed\x105\x02T\x1a\x01\xa1@\x04\xd5\
+\xfb\xdb\x81\xd2i)\x1c\xe3\xa3q\xf7\xb4xh5\x04\
+\xdf\x1c\xe7X\xff\x11\xf3\x1c\xfe\xe6\x93j\xb9b\xdb\xd2\
+M\xaf?\xf5\xea\xa4I\xf7\x1ae\xad\xe9a\x01\x84B\
+\x91\xd6BP\xdf\xc4\xc1\xae&\x8a\xf0\xa7\x92\x8d\xcb\xbe\
+\xbd\xe9\xce\xfbbUa\xdd\x22\x01\xc4\x0dAY]\xbc\
+~EC\xaf\x80\x1a\xecp\xa8\xd3\x912\x0b`#\x04\
+\x82\xbf\x16o(\xf8\x10\x00~\xe2\xc8\xd5Y\x18\xb9\xac\
+l\xe3\xf2\xdd\x99Y9\x93\x01:\x81P\xe9\xf9\xe2\xc2\
+gw\xdf<\xfd\xfe\x91\x22W\xcd\xa6\x1c\xc7I\xb8u\
+\xa5j\xf8\xaf\xfb\xa9\xd5\xdam\xa3\xaf\x1a\x16?q\x04\
+%}\xe3\x08\xea\x1a#xiC=\xde~\xbf]\xdf\
+\xee\xdd\xb8\xca\x04\x83U\xdb1\xaeR\x14\x0e_s\x00\
+\x9e\x86\x00~>\xda\x8c;\xa7\xc4bP?=\x8e4\
+rl\xfa\x9c){\xf6|\xed\xd5\xb1\xc6\xc9\x87\x8f~\
+\xf2\xb9\xa1\xadu\x16\xe7,\x81I\xaaW\xca6?s\
+d\xc2\xd4\xdc\x0c\x22\xe07\x10\xc8\x8e\xad\x85\xcb\xdf\xbd\
+yZ\xee\x90\xe6\x8a\xc0\xc1\x93Z\xdb\xcd\xb7\xdd?D\
+T\xd4\xb3\x00\xb2w[\xd1\xb2W\xce\xec\x0f;\x015\
+nF\xf6\x15\x22\x13~\xcb\x18\xd9\xa9'\xd5N\xa7\xd3\
+\xa9\x00@\xe6\xad\xf3\xc7q\xca&\x11\x10?\xe7\x5c\xab\
+\x92#y\x9b6\xadr\x8d\x9b\x99m\xa2\xb2\xb0\x10\x5c\
+p\xcb\xb2\xef\x8d\xedo\xad\xae\x9b\xe0\xc8\x99\x03JF\
+1\x905\x833\x97)M\xae\x06\xe7O\x86D\xa7\xce\
+\xbcN\x80Q\x0b\xec\xfa\xc6\x8f\xd5o\xd4c\xdf\xa1\xef\
+N\xf1\xa9Cc!\xa8\x050\x99\xc1\xef\x0e\xa1\xf9\xb8\
+\x17}Su\x98{w\x02\xae\x1cfDD\x02\xde\xda\
+\xc5\xb0\xe3\xcb\x96\xe3\x01n\xb9\xab\xed\x1f\x0b\xea\x18!\
+\xd9\x9c\x93\xdd\xdb\x9c\xf9/\x8d\x9d\x91\x93\xa6b\xe4>\
+\x80\x8a\x11\xce\x9e\xdc\xee\xcco\xc9\xbcm~4g\xca\
+\xc3\x04\x82\x9f16\x94\x10\xf2\xd2\xd6\x0d\xcbKO\xd3\
+\xcf\xef\xa6\x84\x8f!\x84\xac*.\xcc\xdf\xd5\x09\xa8\x89\
+\x8e\xb9}\x00q\x11'Ju B\x9e\xdf\xb9\xb9\xc0\
+\x03\x00\x99\xb7\xe5\x0e\xe1\x0a\xf2@\xf0\x191\x07V\x95\
+\xac^\xdd6ujnFD\x85\xdf\x00\xd0\x81\xa3\x0f\
+\xa7d\xe9\xb6\xc2\xe5\x1fMt\xe4\xfc\x84\x80\xdc\xa3\x00\
+\x9bJ\x9d\xf9o\x8fsd\x0f\x16\x88\xf0\x18\x08\xf9\x22\
+i\xdc3\x95Q\x22\x16\xffb0I\x1f;\x9c\x82\x12\
+\xa0\xec\xbd\x16\xac\xd9P\x8f\xc6f\xa9\xc7q\x95,)\
+\xa8;\xd4\x02\xabI\xc0\xad\xe3\xa2\xe1\x98\x10\x03\xbd\x96\
+\xa2\xec+\x86\xed_\xf3\xa3m\x11\xb6\xb8n\xc7\xc3j\
+p>L&l\xc9\xdb\x85\x05\x15\x93\xa6\xcfs0\xce\
+o\x13\x80\xe7\xb7l\xc8\xdf1\xc9\xf1\xc0@\x85\x0a\x8b\
+\x08\xb8\x97p\xae\x85\x22\x14\x94l\x5c\xf6\xed\xec\xd9\xb3\
+U5n\xfd\xaf\x08\xc1U\x22\xc5\xe2\xcd\x85\xed\xca\xe6\
+\x09\x15\xf7^\x02>\x98\x04\xd9#\xc5\xc5+\x1a\xc8\xc9\
+Y\x9e \x10\xba\xa5p\xf9q\x00\xb8a\xca\xc3v\x9d\
+*2\x0b\xe0\x86\x88D\x9f\xfb\xdb\xa6\xa5\x8d]\xce\x1c\
+L\xbb\x7f\xc8\xf6\x8d\xab\xf6M\xbd}~jD\xe6\x0f\
+r\xa242\xd1Z`\x88\xec\x0b\x04y\x9f\xf9 J\
+\x9c\xa4`\xe9\xf6\xb7\x0a\xea&L\xcf\x19\xa4QG=\
+\xa2\x1f>\xab6.>%w\xc6\xb5T\xbc2\x9d\xc2\
+\xed\x95\xb1\xe5\x1d\x17^,\xac\xefV\xf9\x94%\x05Y\
+\xe3\xa3qoV<\x8cQ\x02\x0e\xd6q\xbc\xf6!\xe3\
+\x87k\xfck\x02\xbb\x96E\x0b\x01\xdf#\x9b6\x15\x1c\
+\x18;#'\x8d*\xc8\x11\x08\x91}\x8d\xbb\x17\x99L\
+W\xeb\x98F\xba\x97\x10\x0cP\xe4\xc8\x92\xd2\xb7VU\
+\x8ew\xe4\x8c(u\x16\xec9\xfb\x88\x8e\xce\xe1`j\
+I#>\xfa\xb7uK\x03'?\xf7\xd3\xb0\xf7\x9fo\
+\xfc\xd9\xdd\x85\xa3\xc6e\xe5\x5c'p\xba\x90P\xf2\xa7\
+\x92\xa2e\xdd\xfe\xd3r\xdc\xb8l\x8dh\x14\xe7\x03$\
+>\xc2y\xdevg~\xcb\xa4\xe993\x19\xe8\x14\x81\
+(\xcfl)\x5c\xf1\xe9\x98\xc99\x16\x83\x9a>\x03\xc2\
+\x9bi\xd0\xfbdq\xf1\xcb\xbe\x19\xf9\xc14\xb5V\x95\
+wy\x1f\xdc}\xe7u\x14q\xe6v\xfeZ\xfab\x0d\
+>\xd9\xdd\xda\xe9\x1e\x83\xfa\xea\x913+\x09C\x07\xe8\
+\xd1\x1a\x04^\xda\xa9\xe0\xcbc\xfc\xcd\x18\x8b\xf8P\xfe\
+LrtT\xe6l}\xbc\xce\x90\x0d\xf0>\x8c\xcby\
+e\xceg\x9bnq\xe4^\xce\x80<F\xe1\xdcV\x94\
+_8~\xea\xfd\xa9\x82\xa8\xcef\x04\x81\xab\x06\x99\x1f\
+\xcb\xcb\xcb\xeb\xb6\xf1\x9b4)\xb7\x1f\xd7\xf0\xa7\x89@\
+\x9e:}\xdbu\x9f\xf58'=\x0f>s\xa6\x82\x93\
+\xe9\x84\xd3g\xb7:\x97\xfd\xb3}\xac5o\x1a\xe7\xfc\
+A\x05\xda\xcc2\xe7SM\xe3\xa7\xde\x9fJ\x05\xf5\xfb\
+\x80r\xc7V\xe7\xca\x7f\x9ey\x8d9/\xf1\x81~I\
+.\xbaa0\x1d>\xf5J\x0a\xa3\x0e\xd8s \x80\xaf\
+\xf7\x07\x00\x02\x0c\xe9\xaf\xc3\xa8\xcb\x8d\x88\xc8@\xc9n\
+\x86w\xf6\xf1c\x01?\xbf\xf7\xb5l\xd5\xce.\xfe\xdc\
+>\x7f\x14\x95\x95\x22\xceC?\xdf\xea|\xa1\xe6F\xc7\
+Cf-\x09\xbfK\x08\x7f\xb1\xa4h\xc5_\x00`\xca\
+\xf4\x05C%\xae<N\x08^-)\xca\xdf\xf2/\xed\
+\xf5\xc6:\xe6\x0c\x14\x89\xf6)\x02\xea,\xd9\xb0l\xfd\
+YC\x98\x93\xc79\xc8\x97\xdb\x8a\x96\x17\xdc\xe8\x98\xdb\
+GK\xe8\xef\xc1P\xedo\xde\xb3x\xe7\xce\x9d]\xfa\
+\x9b\xd9/+sT\x94/\x1c?\x82\xa4\x8d\x1f\xde\xb9\
+\xfe\xfd\xaa\x92\xe3/\x7fW<A\x89>\xf6\xcb\xb4\xa6\
+\x17\xef\xba9>p\xe6x\xcd\x10\xeb]\x00\xf0t\x95\
+\xa4[\xb8i\xd3\x93-\x99\x8e\xdc;\x002Y\xa0\xe2\
+\xc2\xcdE\xcf\x1c\xe9\x02jV\xce\xbd\x00\xb9\x99E\xc4\
+\xdf\x95m\xee\xfa\xfd9\x035qz\xce\xf3`\xdcK\
+\xc3\x9a'\x8b\x8b\x97t{$e\xf2\x8cEi2\x0f\
+\x95\x10\xd0\x87K\x8a\x96o\x05\x80\x89\xd3\xe6\xf6\x87 \
+\xfcU\xe0X\xb2\xc5\x99\xdf\xe5\x9f\x9a\xab\xde\xe3\x86\x0f\
+\xf6\xcb\xf3\x93\xcc\xe4\xd11\x83\x89\xa8\x12\x80\xcf\x8e2\
+^\xd9L\xfe\xaa\x88B\xce\xda_\x11\xcf\xd9&D\x9c\
+\x09\x8fPB\x1f:I\x11\x99\xd3\xe7M\x04\xf8\x1f\x05\
+\xc2\x1d'\x89\xf9l6*s\xb6>!*\xeaa\xae\
+\x10\xc2\xfc\xd2\x13ee\xcf\x86\xcf\x1bP'\x07\x9e\xdd\
+n\xc7\x09s\xac\xd4\xa0\xcf\x01W\xe2|\x11,:\x99\
+9o\xbf}\x8e\xb5U\xd1<\x0a\x0e\x9f\xe2\xcbx\xaa\
+\xacln\xb7N-\xde\xc2\xe3\x0e7*\xd7\x1850\
+E\x10\xdc\xfe\xc2]\xc6\xc6\xee\xd6\xdes\xcf=\xda\xe6\
+\xa0\xed1\xce\xb9A\xa4\xe4\x7fO&\xa2)S\xee\x8b\
+\x8d\xa8u\xbf'\x9c\xb8\xfc*q\xcd\xce\xd7\x97Tw\
+\xcf\xb3+5=\xf9s^e\x961c\xc6\x88\xa6\xd8\
+Q3\x15\xb0_\x10EY\xbc\xf5\xc48~\xd4\xa8\xd9\
+\xaa\x84\x8c\xa8\xdf\x82\xf0\xab\x08\x17\xf3J\x9cK\x8f\xfd\
++t\xae\x89w>\xd4\x07\xe1\xf0\x13`d\xe7\xd67\
+\xf3\xd7\x9e\x94[\xc7\xde\xb1p\xa0\x18\x91\x1e\x22\x9c\xff\
+\xa3\xc4Y\xb0\xf6\x82\xeaQ\xe3fd_!0\xf1\x11\
+\xae\xf0\xd2m\x1b\x0b^\xee\xe8\xffn\xcd\xbd\x89S>\
+\x93\x02\xabOV\xf6\xffj\x9bp\xeb\xdck\x09\x11\x1e\
+\xa6`/\x16;W\x14\x9fJ<\x0f8\x08\xa7\xf7@\
+\x22\xbf\xdb\xbai\xf9W\x17\x06\xa8\x99\x0b\x93=\x11\xc9\
+\xf5\xb13?\x08\x00\xe3\x1c\xd9\x83E*\xce\x03#\xc7\
+J\x9c\xcb\x17\xe3\xdfl\xb3g\xcfV\xd5y\xa3~\xc3\
+\x19\x06\x10H+N6\xba?q\xe4\xeabDf\xea\
+\xae\x8f\xfb\xb7l\xbd\x8e\x1a\xc4\x91;V!d\x8c\x10\
+R\xf2\x8b\x8b\x7f\xb8C\xe7\xc3\xc69\xb2c\x04\xaaZ\
+\xc4\x99\xf2\xfe\xb6\xd3\xa2\xeb\xe2\xb0\xb3\x89\xd5\x17\xd8\xf2\
+\xf2\xf2(.\xd9%\xbbd\x97\xec?\xc0\xfe\x0f\x14\xd5\
+\xea\x92\xe4\xa3Oi\x00\x00\x00\x00IEND\xaeB\
+`\x82\
+\x00\x00\x012\
+<\
+svg width=\x2248\x22 h\
+eight=\x2248\x22 viewB\
+ox=\x220 0 48 48\x22 f\
+ill=\x22none\x22 xmlns\
+=\x22http://www.w3.\
+org/2000/svg\x22>\x0a<\
+path d=\x22M12.4501\
+ 37.65L10.3501 3\
+5.55L21.9001 24L\
+10.3501 12.45L12\
+.4501 10.35L24.0\
+001 21.9L35.5501\
+ 10.35L37.6501 1\
+2.45L26.1001 24L\
+37.6501 35.55L35\
+.5501 37.65L24.0\
+001 26.1L12.4501\
+ 37.65Z\x22 fill=\x22#\
+667085\x22/>\x0a</svg>\
+\x0a\
+\x00\x00\x04\x83\
+<\
+svg width=\x2248\x22 h\
+eight=\x2248\x22 viewB\
+ox=\x220 0 48 48\x22 f\
+ill=\x22none\x22 xmlns\
+=\x22http://www.w3.\
+org/2000/svg\x22>\x0a<\
+path d=\x22M10.3929\
+ 26.4C9.73097 26\
+.4 9.16667 26.16\
+43 8.7 25.6929C8\
+.23333 25.2215 8\
+ 24.6548 8 23.99\
+29C8 23.3309 8.2\
+357 22.7666 8.70\
+71 22.3C9.17847 \
+21.8333 9.74513 \
+21.6 10.4071 21.\
+6C11.069 21.6 11\
+.6333 21.8357 12\
+.1 22.3071C12.56\
+67 22.7784 12.8 \
+23.3451 12.8 24.\
+0071C12.8 24.669\
+ 12.5643 25.2333\
+ 12.0929 25.7C11\
+.6215 26.1666 11\
+.0549 26.4 10.39\
+29 26.4ZM23.9929\
+ 26.4C23.331 26.\
+4 22.7667 26.164\
+3 22.3 25.6929C2\
+1.8333 25.2215 2\
+1.6 24.6548 21.6\
+ 23.9929C21.6 23\
+.3309 21.8357 22\
+.7666 22.3071 22\
+.3C22.7785 21.83\
+33 23.3451 21.6 \
+24.0071 21.6C24.\
+669 21.6 25.2333\
+ 21.8357 25.7 22\
+.3071C26.1667 22\
+.7784 26.4 23.34\
+51 26.4 24.0071C\
+26.4 24.669 26.1\
+643 25.2333 25.6\
+929 25.7C25.2215\
+ 26.1666 24.6549\
+ 26.4 23.9929 26\
+.4ZM37.5929 26.4\
+C36.931 26.4 36.\
+3667 26.1643 35.\
+9 25.6929C35.433\
+3 25.2215 35.2 2\
+4.6548 35.2 23.9\
+929C35.2 23.3309\
+ 35.4357 22.7666\
+ 35.9071 22.3C36\
+.3785 21.8333 36\
+.9451 21.6 37.60\
+71 21.6C38.269 2\
+1.6 38.8333 21.8\
+357 39.3 22.3071\
+C39.7667 22.7784\
+ 40 23.3451 40 2\
+4.0071C40 24.669\
+ 39.7643 25.2333\
+ 39.2929 25.7C38\
+.8215 26.1666 38\
+.2549 26.4 37.59\
+29 26.4Z\x22 fill=\x22\
+#667085\x22/>\x0a</svg\
+>\x0a\
+\x00\x00\x037\
+<\
+svg width=\x2224\x22 h\
+eight=\x2224\x22 viewB\
+ox=\x220 0 24 24\x22 f\
+ill=\x22none\x22 xmlns\
+=\x22http://www.w3.\
+org/2000/svg\x22>\x0a<\
+path d=\x22M0 12C0 \
+5.37258 5.37258 \
+0 12 0C18.6274 0\
+ 24 5.37258 24 1\
+2C24 18.6274 18.\
+6274 24 12 24C5.\
+37258 24 0 18.62\
+74 0 12Z\x22 fill=\x22\
+#E6E6E6\x22/>\x0a<path\
+ d=\x22M15.5 12C16.\
+3284 12 17 12.67\
+16 17 13.5V14C17\
+ 15.9714 15.1405\
+ 18 12 18C8.8595\
+1 18 7 15.9714 7\
+ 14V13.5C7 12.67\
+16 7.67157 12 8.\
+5 12H15.5ZM15.5 \
+13H8.5C8.22386 1\
+3 8 13.2239 8 13\
+.5V14C8 15.4376 \
+9.43216 17 12 17\
+C14.5678 17 16 1\
+5.4376 16 14V13.\
+5C16 13.2239 15.\
+7761 13 15.5 13Z\
+M12 5.5C13.5188 \
+5.5 14.75 6.7312\
+2 14.75 8.25C14.\
+75 9.76878 13.51\
+88 11 12 11C10.4\
+812 11 9.25 9.76\
+878 9.25 8.25C9.\
+25 6.73122 10.48\
+12 5.5 12 5.5ZM1\
+2 6.5C11.0335 6.\
+5 10.25 7.2835 1\
+0.25 8.25C10.25 \
+9.2165 11.0335 1\
+0 12 10C12.9665 \
+10 13.75 9.2165 \
+13.75 8.25C13.75\
+ 7.2835 12.9665 \
+6.5 12 6.5Z\x22 fil\
+l=\x22#616161\x22/>\x0a</\
+svg>\x0a\x0a\
+\x00\x00\x00\xdf\
+<\
+svg width=\x2248\x22 h\
+eight=\x2248\x22 viewB\
+ox=\x220 0 48 48\x22 f\
+ill=\x22none\x22 xmlns\
+=\x22http://www.w3.\
+org/2000/svg\x22>\x0a<\
+path d=\x22M18.9002\
+ 35.7L7.7002 24.\
+5L9.8502 22.35L1\
+8.9002 31.4L38.1\
+002 12.2L40.2502\
+ 14.35L18.9002 3\
+5.7Z\x22 fill=\x22#667\
+085\x22/>\x0a</svg>\x0a\
+\x00\x00\x01V\
+<\
+svg width=\x2248\x22 h\
+eight=\x2248\x22 viewB\
+ox=\x220 0 48 48\x22 f\
+ill=\x22none\x22 xmlns\
+=\x22http://www.w3.\
+org/2000/svg\x22>\x0a<\
+path d=\x22M9 42C8.\
+2 42 7.5 41.7 6.\
+9 41.1C6.3 40.5 \
+6 39.8 6 39V9C6 \
+8.2 6.3 7.5 6.9 \
+6.9C7.5 6.3 8.2 \
+6 9 6H23.55V9H9V\
+39H23.55V42H9ZM3\
+3.3 32.75L31.15 \
+30.6L36.25 25.5H\
+18.75V22.5H36.15\
+L31.05 17.4L33.2\
+ 15.25L42 24.05L\
+33.3 32.75Z\x22 fil\
+l=\x22#667085\x22/>\x0a</\
+svg>\x0a\
+\x00\x00\x01f\
+<\
+svg width=\x2248\x22 h\
+eight=\x2248\x22 viewB\
+ox=\x220 0 48 48\x22 f\
+ill=\x22none\x22 xmlns\
+=\x22http://www.w3.\
+org/2000/svg\x22>\x0a<\
+path d=\x22M24.45 4\
+2V39H39V9H24.45V\
+6H39C39.8 6 40.5\
+ 6.3 41.1 6.9C41\
+.7 7.5 42 8.2 42\
+ 9V39C42 39.8 41\
+.7 40.5 41.1 41.\
+1C40.5 41.7 39.8\
+ 42 39 42H24.45Z\
+M20.55 32.75L18.\
+4 30.6L23.5 25.5\
+H6V22.5H23.4L18.\
+3 17.4L20.45 15.\
+25L29.25 24.05L2\
+0.55 32.75Z\x22 fil\
+l=\x22#667085\x22/>\x0a</\
+svg>\x0a\
+"
+
+qt_resource_name = b"\
+\x00\x02\
+\x00\x00\x07\x84\
+\x00q\
+\x00t\
+\x00\x03\
+\x00\x00x<\
+\x00q\
+\x00m\x00l\
+\x00\x0c\
+\x0fN\xa7E\
+\x00C\
+\x00o\x00l\x00o\x00r\x00P\x00a\x00l\x00e\x00t\x00t\x00e\
+\x00\x05\
+\x00o\xa6S\
+\x00i\
+\x00c\x00o\x00n\x00s\
+\x00\x08\
+\x03\xc6T'\
+\x00p\
+\x00l\x00u\x00s\x00.\x00s\x00v\x00g\
+\x00\x0c\
+\x07\x11\xd4\xa7\
+\x00u\
+\x00s\x00e\x00r\x00M\x00a\x00s\x00k\x00.\x00s\x00v\x00g\
+\x00\x06\
+\x07\x87WG\
+\x00q\
+\x00t\x00.\x00p\x00n\x00g\
+\x00\x0a\
+\x08\xab\xd7\x87\
+\x00u\
+\x00p\x00d\x00a\x00t\x00e\x00.\x00s\x00v\x00g\
+\x00\x0a\
+\x0c\xad\x02\x87\
+\x00d\
+\x00e\x00l\x00e\x00t\x00e\x00.\x00s\x00v\x00g\
+\x00\x08\
+\x0b\x07W\xa7\
+\x00e\
+\x00d\x00i\x00t\x00.\x00s\x00v\x00g\
+\x00\x0e\
+\x05\x92p\xc7\
+\x00t\
+\x00e\x00s\x00t\x00s\x00e\x00r\x00v\x00e\x00r\x00.\x00p\x00n\x00g\
+\x00\x09\
+\x06\x98\x8e\xa7\
+\x00c\
+\x00l\x00o\x00s\x00e\x00.\x00s\x00v\x00g\
+\x00\x08\
+\x06\xb6W\xa7\
+\x00d\
+\x00o\x00t\x00s\x00.\x00s\x00v\x00g\
+\x00\x08\
+\x09\xc5UG\
+\x00u\
+\x00s\x00e\x00r\x00.\x00s\x00v\x00g\
+\x00\x06\
+\x07^Z\xc7\
+\x00o\
+\x00k\x00.\x00s\x00v\x00g\
+\x00\x0a\
+\x06\xc91\x07\
+\x00l\
+\x00o\x00g\x00o\x00u\x00t\x00.\x00s\x00v\x00g\
+\x00\x09\
+\x0e\x01\xbcg\
+\x00l\
+\x00o\x00g\x00i\x00n\x00.\x00s\x00v\x00g\
+"
+
+qt_resource_struct = b"\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00\x0a\x00\x02\x00\x00\x00\x01\x00\x00\x00\x03\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00\x16\x00\x02\x00\x00\x00\x01\x00\x00\x00\x04\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x004\x00\x02\x00\x00\x00\x0d\x00\x00\x00\x05\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00D\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
+\x00\x00\x01\x8d\x87\xa2.\x0a\
+\x00\x00\x00\xd4\x00\x00\x00\x00\x00\x01\x00\x00\x13\x96\
+\x00\x00\x01\x8d\x87\xa2.\x0a\
+\x00\x00\x00\xf6\x00\x00\x00\x00\x00\x01\x00\x00.-\
+\x00\x00\x01\x8d\x87\xa2.\x0a\
+\x00\x00\x01\x0e\x00\x00\x00\x00\x00\x01\x00\x00/c\
+\x00\x00\x01\x8d\x87\xa2.\x0a\
+\x00\x00\x01L\x00\x00\x00\x00\x00\x01\x00\x008\x08\
+\x00\x00\x01\x8d\x87\xa2.\x0a\
+\x00\x00\x00Z\x00\x00\x00\x00\x00\x01\x00\x00\x00\xc4\
+\x00\x00\x01\x8d\x87\xa2.\x0a\
+\x00\x00\x01:\x00\x00\x00\x00\x00\x01\x00\x007%\
+\x00\x00\x01\x8d\x87\xa2.\x0a\
+\x00\x00\x00x\x00\x00\x00\x00\x00\x01\x00\x00\x01\x5c\
+\x00\x00\x01\x8d\x87\xa2.\x0a\
+\x00\x00\x00\x8a\x00\x00\x00\x00\x00\x01\x00\x00\x0c\xf3\
+\x00\x00\x01\x8d\x87\xa2.\x0a\
+\x00\x00\x01$\x00\x00\x00\x00\x00\x01\x00\x003\xea\
+\x00\x00\x01\x8d\x87\xa2.\x0a\
+\x00\x00\x00\xbe\x00\x00\x00\x00\x00\x01\x00\x00\x11\x9b\
+\x00\x00\x01\x8d\x87\xa2.\x0a\
+\x00\x00\x00\xa4\x00\x00\x00\x00\x00\x01\x00\x00\x0f\xe4\
+\x00\x00\x01\x8d\x87\xa2.\x0a\
+\x00\x00\x01f\x00\x00\x00\x00\x00\x01\x00\x009b\
+\x00\x00\x01\x8d\x87\xa2.\x0a\
+"
+
+def qInitResources():
+ QtCore.qRegisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+def qCleanupResources():
+ QtCore.qUnregisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+qInitResources()
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import Property, Signal, ClassInfo
+from PySide6.QtNetwork import (QNetworkAccessManager, QRestAccessManager,
+ QNetworkRequestFactory, QSslSocket)
+from PySide6.QtQml import QmlElement, QPyQmlParserStatus, ListProperty
+from abstractresource import AbstractResource
+
+QML_IMPORT_NAME = "ColorPalette"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+@ClassInfo(DefaultProperty="resources")
+class RestService(QPyQmlParserStatus):
+
+ urlChanged = Signal()
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.m_resources = []
+ self.m_qnam = QNetworkAccessManager()
+ self.m_qnam.setAutoDeleteReplies(True)
+ self.m_manager = QRestAccessManager(self.m_qnam)
+ self.m_serviceApi = QNetworkRequestFactory()
+
+ @Property(str, notify=urlChanged)
+ def url(self):
+ return self.m_serviceApi.baseUrl()
+
+ @url.setter
+ def url(self, url):
+ if self.m_serviceApi.baseUrl() != url:
+ self.m_serviceApi.setBaseUrl(url)
+ self.urlChanged.emit()
+
+ @Property(bool, constant=True)
+ def sslSupported(self):
+ return QSslSocket.supportsSsl()
+
+ def classBegin(self):
+ pass
+
+ def componentComplete(self):
+ for resource in self.m_resources:
+ resource.setAccessManager(self.m_manager)
+ resource.setServiceApi(self.m_serviceApi)
+
+ def appendResource(self, r):
+ self.m_resources.append(r)
+
+ resources = ListProperty(AbstractResource, appendResource)
menu = self.addMenu("Json")
tb = self.addToolBar("Json Actions")
- zoomInIcon = QIcon.fromTheme("zoom-in")
+ zoomInIcon = QIcon.fromTheme(QIcon.ThemeIcon.ZoomIn)
a = menu.addAction(zoomInIcon, "&+Expand all", self._tree.expandAll)
tb.addAction(a)
a.setPriority(QAction.LowPriority)
a.setShortcut(QKeySequence.New)
- zoomOutIcon = QIcon.fromTheme("zoom-out")
+ zoomOutIcon = QIcon.fromTheme(QIcon.ThemeIcon.ZoomOut)
a = menu.addAction(zoomOutIcon, "&-Collapse all", self._tree.collapseAll)
tb.addAction(a)
a.setPriority(QAction.LowPriority)
actionZoomIn = self._toolBar.addAction("Zoom in")
actionZoomIn.setToolTip("Increase zoom level")
- actionZoomIn.setIcon(QIcon(":/demos/documentviewer/images/zoom-in.png"))
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.ZoomIn,
+ QIcon(":/demos/documentviewer/images/zoom-in.png"))
+ actionZoomIn.setIcon(icon)
self._toolBar.addAction(actionZoomIn)
actionZoomIn.triggered.connect(self.onActionZoomInTriggered)
actionZoomOut = self._toolBar.addAction("Zoom out")
actionZoomOut.setToolTip("Decrease zoom level")
- actionZoomOut.setIcon(QIcon(":/demos/documentviewer/images/zoom-out.png"))
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.ZoomIn,
+ QIcon(":/demos/documentviewer/images/zoom-out.png"))
+ actionZoomOut.setIcon(icon)
self._toolBar.addAction(actionZoomOut)
actionZoomOut.triggered.connect(self.onActionZoomOutTriggered)
def setupTxtUi(self):
editMenu = self.addMenu("Edit")
editToolBar = self.addToolBar("Edit")
- cutIcon = QIcon.fromTheme("edit-cut",
+ cutIcon = QIcon.fromTheme(QIcon.ThemeIcon.EditCut,
QIcon(":/demos/documentviewer/images/cut.png"))
cutAct = QAction(cutIcon, "Cut", self)
cutAct.setShortcuts(QKeySequence.Cut)
editMenu.addAction(cutAct)
editToolBar.addAction(cutAct)
- copyIcon = QIcon.fromTheme("edit-copy",
+ copyIcon = QIcon.fromTheme(QIcon.ThemeIcon.EditCopy,
QIcon(":/demos/documentviewer/images/copy.png"))
copyAct = QAction(copyIcon, "Copy", self)
copyAct.setShortcuts(QKeySequence.Copy)
editMenu.addAction(copyAct)
editToolBar.addAction(copyAct)
- pasteIcon = QIcon.fromTheme("edit-paste",
+ pasteIcon = QIcon.fromTheme(QIcon.ThemeIcon.EditPaste,
QIcon(":/demos/documentviewer/images/paste.png"))
pasteAct = QAction(pasteIcon, "Paste", self)
pasteAct.setShortcuts(QKeySequence.Paste)
################################################################################
## Form generated from reading UI file 'mainwindow.ui'
##
-## Created by: Qt User Interface Compiler version 6.5.3
+## Created by: Qt User Interface Compiler version 6.7.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self.splitter.addWidget(self.tabWidget)
self.scrollArea = QScrollArea(self.splitter)
self.scrollArea.setObjectName(u"scrollArea")
- sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ sizePolicy = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.scrollArea.sizePolicy().hasHeightForWidth())
MainWindow.setStatusBar(self.statusbar)
self.mainToolBar = QToolBar(MainWindow)
self.mainToolBar.setObjectName(u"mainToolBar")
- MainWindow.addToolBar(Qt.TopToolBarArea, self.mainToolBar)
+ MainWindow.addToolBar(Qt.ToolBarArea.TopToolBarArea, self.mainToolBar)
self.menubar.addAction(self.qtFileMenu.menuAction())
self.menubar.addAction(self.menuHelp.menuAction())
-Task Menu Extension (Designer)
-==============================
+.. _task-menu-extension-example:
+
+Task Menu Extension Example
+===========================
This example shows how to add custom widgets to Qt Designer,
which can be launched with `pyside6-designer`, and to extend
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Layouts
+import QtGraphs
+
+Item {
+ id: mainView
+ width: 1280
+ height: 720
+
+ RowLayout {
+ id: graphsRow
+
+ readonly property real margin: mainView.width * 0.02
+
+ anchors.fill: parent
+ anchors.margins: margin
+ spacing: margin
+
+ Rectangle {
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ color: "#262626"
+ border.color: "#4d4d4d"
+ border.width: 1
+ radius: graphsRow.margin
+ //! [bargraph]
+ GraphsView {
+ anchors.fill: parent
+ anchors.margins: 16
+ theme: GraphTheme {
+ colorTheme: GraphTheme.ColorThemeDark
+ }
+ //! [bargraph]
+ //! [barseries]
+ BarSeries {
+ axisX: BarCategoryAxis {
+ categories: [2024, 2025, 2026]
+ gridVisible: false
+ minorGridVisible: false
+ }
+ axisY: ValueAxis {
+ min: 20
+ max: 100
+ tickInterval: 10
+ minorTickCount: 9
+ }
+ //! [barseries]
+ //! [barset]
+ BarSet {
+ values: [82, 50, 75]
+ borderWidth: 2
+ color: "#373F26"
+ borderColor: "#DBEB00"
+ }
+ //! [barset]
+ }
+ }
+ }
+
+ Rectangle {
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ color: "#262626"
+ border.color: "#4d4d4d"
+ border.width: 1
+ radius: graphsRow.margin
+
+ //! [linegraph]
+ GraphsView {
+ anchors.fill: parent
+ anchors.margins: 16
+ theme: GraphTheme {
+ readonly property color c1: "#DBEB00"
+ readonly property color c2: "#373F26"
+ readonly property color c3: Qt.lighter(c2, 1.5)
+ colorTheme: GraphTheme.ColorThemeDark
+ gridMajorBarsColor: c3
+ gridMinorBarsColor: c2
+ axisXMajorColor: c3
+ axisYMajorColor: c3
+ axisXMinorColor: c2
+ axisYMinorColor: c2
+ axisXLabelsColor: c1
+ axisYLabelsColor: c1
+ }
+ //! [linegraph]
+
+ //! [linemarker]
+ component Marker : Rectangle {
+ width: 16
+ height: 16
+ color: "#ffffff"
+ radius: width * 0.5
+ border.width: 4
+ border.color: "#000000"
+ }
+ //! [linemarker]
+
+ //! [lineseriestheme]
+ SeriesTheme {
+ id: seriesTheme
+ colors: ["#2CDE85", "#DBEB00"]
+ }
+ //! [lineseriestheme]
+
+ //! [lineseries1]
+ LineSeries {
+ id: lineSeries1
+ theme: seriesTheme
+ axisX: ValueAxis {
+ max: 5
+ tickInterval: 1
+ minorTickCount: 9
+ labelDecimals: 1
+ }
+ axisY: ValueAxis {
+ max: 10
+ tickInterval: 1
+ minorTickCount: 4
+ labelDecimals: 1
+ }
+ width: 4
+ pointMarker: Marker { }
+ XYPoint { x: 0; y: 0 }
+ XYPoint { x: 1; y: 2.1 }
+ XYPoint { x: 2; y: 3.3 }
+ XYPoint { x: 3; y: 2.1 }
+ XYPoint { x: 4; y: 4.9 }
+ XYPoint { x: 5; y: 3.0 }
+ }
+ //! [lineseries1]
+
+ //! [lineseries2]
+ LineSeries {
+ id: lineSeries2
+ theme: seriesTheme
+ width: 4
+ pointMarker: Marker { }
+ XYPoint { x: 0; y: 5.0 }
+ XYPoint { x: 1; y: 3.3 }
+ XYPoint { x: 2; y: 7.1 }
+ XYPoint { x: 3; y: 7.5 }
+ XYPoint { x: 4; y: 6.1 }
+ XYPoint { x: 5; y: 3.2 }
+ }
+ //! [lineseries2]
+ }
+ }
+ }
+}
--- /dev/null
+module HelloGraphs
+Main 1.0 Main.qml
--- /dev/null
+HelloGraphs Example
+===================
+
+The example shows how to make a simple 2D bar graph and line graph.
+
+BarGraph
+--------
+
+The first graph in the example is a bar graph. Creating it starts with a GraphsView
+component and setting the theme to one which is suitable on
+dark backgrounds. This theme adjusts the graph background grid and axis lines and
+labels.
+
+To make this a bar graph, add a ``BarSeries.`` The X axis of the series is a
+``BarCategoryAxis`` with 3 categories. We hide both the vertical grid and the
+axis lines. The Y axis of the series is ``ValueAxis`` with visible range
+between 20 and 100. Major ticks with labels will be shown on every 10 values
+using the ``tickInterval`` property. Minor ticks will be shown on every 1
+values setting the ``minorTickCount`` propertyt to 9, which means that between
+every major ticks there will be 9 minor ones.
+
+Then data is added into ``BarSeries`` using ``BarSet.`` There are 3 bars, and we define
+custom bars color and border properties. These properties will override the possible
+theme set for the ``AbstractSeries.``
+
+LineGraph
+---------
+
+The second graph of the example is a line graph. It also starts by defining a
+``GraphsView`` element. A custom ``GraphTheme`` is created to get a custom appearance.
+``GraphTheme`` offers quite a wide range of customization possibilities for the background
+grid and axis, which get applied after the ``colorTheme``.
+
+A custom ``Marker`` component is used to visualize the data points.
+
+The previous bar graph didn't define a separate ``SeriesTheme``, so it uses the
+default theme. This line graph uses a custom theme with the desired line colors.
+
+To make this a line graph, add a ``LineSeries.`` The first series defines
+``axisX`` and ``axisY`` for this graph. It also sets the ``pointMarker`` to use
+the custom ``Marker`` component that was created earlier. Data points are added
+using ``XYPoint`` elements.
+
+The second line series is similar to the first. The ``axisX`` and ``axisY``
+don't need to be defined as the graph already contains them. As this is the
+second ``LineSeries`` inside the ``GraphsView``, second color from the
+``seriesTheme`` gets automatically picked.
+
+.. image:: hellographs.webp
+ :width: 1293
+ :alt: HelloGraphs Screenshot
--- /dev/null
+{
+ "files": ["main.py", "HelloGraphs/Main.qml", "HelloGraphs/qmldir"]
+}
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+"""PySide6 port of the Qt Hello Graphs example from Qt v6.x"""
+
+from pathlib import Path
+import sys
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtQuick import QQuickView
+
+
+if __name__ == '__main__':
+ app = QGuiApplication(sys.argv)
+
+ viewer = QQuickView()
+ viewer.engine().addImportPath(Path(__file__).parent)
+ viewer.setColor("black")
+ viewer.loadFromModule("HelloGraphs", "Main")
+ viewer.show()
+ r = app.exec()
+ del viewer
+ sys.exit(r)
--- /dev/null
+Minimal Surface Example
+=======================
+
+The example shows the minimal code to create a surface.
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import sys
+
+from PySide6.QtCore import QSize
+from PySide6.QtGui import QVector3D
+from PySide6.QtGraphs import (Q3DSurface, QSurfaceDataItem,
+ QSurface3DSeries)
+from PySide6.QtWidgets import QApplication
+from PySide6.QtQuickWidgets import QQuickWidget
+
+
+DESCRIPTION = """Minimal Qt Graphs Surface Example
+
+Use the mouse wheel to zoom. Rotate using the right mouse button.
+"""
+
+
+if __name__ == '__main__':
+ app = QApplication(sys.argv)
+
+ print(DESCRIPTION)
+
+ surface = Q3DSurface()
+ axis = surface.axisX()
+ axis.setTitle("X")
+ axis.setTitleVisible(True)
+ axis = surface.axisY()
+ axis.setTitle("Y")
+ axis.setTitleVisible(True)
+ axis = surface.axisZ()
+ axis.setTitle("Z")
+ axis.setTitleVisible(True)
+
+ data = []
+ data_row1 = [QSurfaceDataItem(QVector3D(0, 0.1, 0.5)),
+ QSurfaceDataItem(QVector3D(1, 0.5, 0.5))]
+ data.append(data_row1)
+ data_row2 = [QSurfaceDataItem(QVector3D(0, 1.8, 1)),
+ QSurfaceDataItem(QVector3D(1, 1.2, 1))]
+ data.append(data_row2)
+
+ series = QSurface3DSeries()
+ series.dataProxy().resetArray(data)
+ surface.addSeries(series)
+
+ available_height = app.primaryScreen().availableGeometry().height()
+ width = available_height * 4 / 5
+ surface.resize(QSize(width, width))
+ surface.setResizeMode(QQuickWidget.SizeRootObjectToView)
+ surface.show()
+
+ sys.exit(app.exec())
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from enum import Enum
+from math import sin, cos, degrees
+
+from PySide6.QtCore import Qt
+from PySide6.QtGraphs import QAbstract3DGraph, Q3DInputHandler
+
+
+class InputState(Enum):
+ StateNormal = 0
+ StateDraggingX = 1
+ StateDraggingZ = 2
+ StateDraggingY = 3
+
+
+class AxesInputHandler(Q3DInputHandler):
+
+ def __init__(self, graph, parent=None):
+ super().__init__(parent)
+ self._mousePressed = False
+ self._state = InputState.StateNormal
+ self._axisX = None
+ self._axisZ = None
+ self._axisY = None
+ self._speedModifier = 15.0
+
+ # Connect to the item selection signal from graph
+ graph.selectedElementChanged.connect(self.handleElementSelected)
+
+ def setAxes(self, axisX, axisZ, axisY):
+ self._axisX = axisX
+ self._axisZ = axisZ
+ self._axisY = axisY
+
+ def setDragSpeedModifier(self, modifier):
+ self._speedModifier = modifier
+
+ def mousePressEvent(self, event, mousePos):
+ super().mousePressEvent(event, mousePos)
+ if Qt.LeftButton == event.button():
+ self._mousePressed = True
+
+ def mouseMoveEvent(self, event, mousePos):
+ # Check if we're trying to drag axis label
+ if self._mousePressed and self._state != InputState.StateNormal:
+ self.setPreviousInputPos(self.inputPosition())
+ self.setInputPosition(mousePos)
+ self.handleAxisDragging()
+ else:
+ super().mouseMoveEvent(event, mousePos)
+
+ def mouseReleaseEvent(self, event, mousePos):
+ super().mouseReleaseEvent(event, mousePos)
+ self._mousePressed = False
+ self._state = InputState.StateNormal
+
+ def handleElementSelected(self, type):
+ if type == QAbstract3DGraph.ElementAxisXLabel:
+ self._state = InputState.StateDraggingX
+ elif type == QAbstract3DGraph.ElementAxisYLabel:
+ self._state = InputState.StateDraggingY
+ elif type == QAbstract3DGraph.ElementAxisZLabel:
+ self._state = InputState.StateDraggingZ
+ else:
+ self._state = InputState.StateNormal
+
+ def handleAxisDragging(self):
+ distance = 0.0
+ # Get scene orientation from active camera
+ xRotation = self.cameraXRotation()
+ yRotation = self.cameraYRotation()
+
+ # Calculate directional drag multipliers based on rotation
+ xMulX = cos(degrees(xRotation))
+ xMulY = sin(degrees(xRotation))
+ zMulX = sin(degrees(xRotation))
+ zMulY = cos(degrees(xRotation))
+
+ # Get the drag amount
+ move = self.inputPosition() - self.previousInputPos()
+
+ # Flip the effect of y movement if we're viewing from below
+ yMove = -move.y() if yRotation < 0 else move.y()
+
+ # Adjust axes
+ if self._state == InputState.StateDraggingX:
+ distance = (move.x() * xMulX - yMove * xMulY) / self._speedModifier
+ self._axisX.setRange(self._axisX.min() - distance,
+ self._axisX.max() - distance)
+ elif self._state == InputState.StateDraggingZ:
+ distance = (move.x() * zMulX + yMove * zMulY) / self._speedModifier
+ self._axisZ.setRange(self._axisZ.min() + distance,
+ self._axisZ.max() + distance)
+ elif self._state == InputState.StateDraggingY:
+ # No need to use adjusted y move here
+ distance = move.y() / self._speedModifier
+ self._axisY.setRange(self._axisY.min() + distance,
+ self._axisY.max() + distance)
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from graphmodifier import GraphModifier
+
+from PySide6.QtCore import QObject, Qt
+from PySide6.QtGui import QFont
+from PySide6.QtWidgets import (QButtonGroup, QCheckBox, QComboBox, QFontComboBox,
+ QLabel, QPushButton, QHBoxLayout, QSizePolicy,
+ QRadioButton, QSlider, QVBoxLayout, QWidget)
+from PySide6.QtQuickWidgets import QQuickWidget
+from PySide6.QtGraphs import (QAbstract3DGraph, QAbstract3DSeries, Q3DBars)
+
+
+class BarGraph(QObject):
+
+ def __init__(self, minimum_graph_size, maximum_graph_size):
+ super().__init__()
+ self._barsGraph = Q3DBars()
+ self._barsWidget = QWidget()
+ hLayout = QHBoxLayout(self._barsWidget)
+ self._barsGraph.setMinimumSize(minimum_graph_size)
+ self._barsGraph.setMaximumSize(maximum_graph_size)
+ self._barsGraph.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ self._barsGraph.setFocusPolicy(Qt.StrongFocus)
+ self._barsGraph.setResizeMode(QQuickWidget.SizeRootObjectToView)
+ hLayout.addWidget(self._barsGraph, 1)
+
+ vLayout = QVBoxLayout()
+ hLayout.addLayout(vLayout)
+
+ themeList = QComboBox(self._barsWidget)
+ themeList.addItem("Qt")
+ themeList.addItem("Primary Colors")
+ themeList.addItem("Digia")
+ themeList.addItem("Stone Moss")
+ themeList.addItem("Army Blue")
+ themeList.addItem("Retro")
+ themeList.addItem("Ebony")
+ themeList.addItem("Isabelle")
+ themeList.setCurrentIndex(0)
+
+ labelButton = QPushButton(self._barsWidget)
+ labelButton.setText("Change label style")
+
+ smoothCheckBox = QCheckBox(self._barsWidget)
+ smoothCheckBox.setText("Smooth bars")
+ smoothCheckBox.setChecked(False)
+
+ barStyleList = QComboBox(self._barsWidget)
+ barStyleList.addItem("Bar", QAbstract3DSeries.Mesh.Bar)
+ barStyleList.addItem("Pyramid", QAbstract3DSeries.Mesh.Pyramid)
+ barStyleList.addItem("Cone", QAbstract3DSeries.Mesh.Cone)
+ barStyleList.addItem("Cylinder", QAbstract3DSeries.Mesh.Cylinder)
+ barStyleList.addItem("Bevel bar", QAbstract3DSeries.Mesh.BevelBar)
+ barStyleList.addItem("Sphere", QAbstract3DSeries.Mesh.Sphere)
+ barStyleList.setCurrentIndex(4)
+
+ cameraButton = QPushButton(self._barsWidget)
+ cameraButton.setText("Change camera preset")
+
+ zoomToSelectedButton = QPushButton(self._barsWidget)
+ zoomToSelectedButton.setText("Zoom to selected bar")
+
+ selectionModeList = QComboBox(self._barsWidget)
+ selectionModeList.addItem("None", QAbstract3DGraph.SelectionNone)
+ selectionModeList.addItem("Bar", QAbstract3DGraph.SelectionItem)
+ selectionModeList.addItem("Row", QAbstract3DGraph.SelectionRow)
+ sel = QAbstract3DGraph.SelectionItemAndRow
+ selectionModeList.addItem("Bar and Row", sel)
+ selectionModeList.addItem("Column", QAbstract3DGraph.SelectionColumn)
+ sel = QAbstract3DGraph.SelectionItemAndColumn
+ selectionModeList.addItem("Bar and Column", sel)
+ sel = QAbstract3DGraph.SelectionRowAndColumn
+ selectionModeList.addItem("Row and Column", sel)
+ sel = QAbstract3DGraph.SelectionItemRowAndColumn
+ selectionModeList.addItem("Bar, Row and Column", sel)
+ sel = QAbstract3DGraph.SelectionSlice | QAbstract3DGraph.SelectionRow
+ selectionModeList.addItem("Slice into Row", sel)
+ sel = QAbstract3DGraph.SelectionSlice | QAbstract3DGraph.SelectionItemAndRow
+ selectionModeList.addItem("Slice into Row and Item", sel)
+ sel = QAbstract3DGraph.SelectionSlice | QAbstract3DGraph.SelectionColumn
+ selectionModeList.addItem("Slice into Column", sel)
+ sel = (QAbstract3DGraph.SelectionSlice
+ | QAbstract3DGraph.SelectionItemAndColumn)
+ selectionModeList.addItem("Slice into Column and Item", sel)
+ sel = (QAbstract3DGraph.SelectionItemRowAndColumn
+ | QAbstract3DGraph.SelectionMultiSeries)
+ selectionModeList.addItem("Multi: Bar, Row, Col", sel)
+ sel = (QAbstract3DGraph.SelectionSlice
+ | QAbstract3DGraph.SelectionItemAndRow
+ | QAbstract3DGraph.SelectionMultiSeries)
+ selectionModeList.addItem("Multi, Slice: Row, Item", sel)
+ sel = (QAbstract3DGraph.SelectionSlice
+ | QAbstract3DGraph.SelectionItemAndColumn
+ | QAbstract3DGraph.SelectionMultiSeries)
+ selectionModeList.addItem("Multi, Slice: Col, Item", sel)
+ selectionModeList.setCurrentIndex(1)
+
+ backgroundCheckBox = QCheckBox(self._barsWidget)
+ backgroundCheckBox.setText("Show background")
+ backgroundCheckBox.setChecked(False)
+
+ gridCheckBox = QCheckBox(self._barsWidget)
+ gridCheckBox.setText("Show grid")
+ gridCheckBox.setChecked(True)
+
+ seriesCheckBox = QCheckBox(self._barsWidget)
+ seriesCheckBox.setText("Show second series")
+ seriesCheckBox.setChecked(False)
+
+ reverseValueAxisCheckBox = QCheckBox(self._barsWidget)
+ reverseValueAxisCheckBox.setText("Reverse value axis")
+ reverseValueAxisCheckBox.setChecked(False)
+
+ reflectionCheckBox = QCheckBox(self._barsWidget)
+ reflectionCheckBox.setText("Show reflections")
+ reflectionCheckBox.setChecked(False)
+
+ rotationSliderX = QSlider(Qt.Horizontal, self._barsWidget)
+ rotationSliderX.setTickInterval(30)
+ rotationSliderX.setTickPosition(QSlider.TicksBelow)
+ rotationSliderX.setMinimum(-180)
+ rotationSliderX.setValue(0)
+ rotationSliderX.setMaximum(180)
+ rotationSliderY = QSlider(Qt.Horizontal, self._barsWidget)
+ rotationSliderY.setTickInterval(15)
+ rotationSliderY.setTickPosition(QSlider.TicksAbove)
+ rotationSliderY.setMinimum(-90)
+ rotationSliderY.setValue(0)
+ rotationSliderY.setMaximum(90)
+
+ fontSizeSlider = QSlider(Qt.Horizontal, self._barsWidget)
+ fontSizeSlider.setTickInterval(10)
+ fontSizeSlider.setTickPosition(QSlider.TicksBelow)
+ fontSizeSlider.setMinimum(1)
+ fontSizeSlider.setValue(30)
+ fontSizeSlider.setMaximum(100)
+
+ fontList = QFontComboBox(self._barsWidget)
+ fontList.setCurrentFont(QFont("Times New Roman"))
+
+ shadowQuality = QComboBox(self._barsWidget)
+ shadowQuality.addItem("None")
+ shadowQuality.addItem("Low")
+ shadowQuality.addItem("Medium")
+ shadowQuality.addItem("High")
+ shadowQuality.addItem("Low Soft")
+ shadowQuality.addItem("Medium Soft")
+ shadowQuality.addItem("High Soft")
+ shadowQuality.setCurrentIndex(5)
+
+ rangeList = QComboBox(self._barsWidget)
+ rangeList.addItem("2015")
+ rangeList.addItem("2016")
+ rangeList.addItem("2017")
+ rangeList.addItem("2018")
+ rangeList.addItem("2019")
+ rangeList.addItem("2020")
+ rangeList.addItem("2021")
+ rangeList.addItem("2022")
+ rangeList.addItem("All")
+ rangeList.setCurrentIndex(8)
+
+ axisTitlesVisibleCB = QCheckBox(self._barsWidget)
+ axisTitlesVisibleCB.setText("Axis titles visible")
+ axisTitlesVisibleCB.setChecked(True)
+
+ axisTitlesFixedCB = QCheckBox(self._barsWidget)
+ axisTitlesFixedCB.setText("Axis titles fixed")
+ axisTitlesFixedCB.setChecked(True)
+
+ axisLabelRotationSlider = QSlider(Qt.Horizontal, self._barsWidget)
+ axisLabelRotationSlider.setTickInterval(10)
+ axisLabelRotationSlider.setTickPosition(QSlider.TicksBelow)
+ axisLabelRotationSlider.setMinimum(0)
+ axisLabelRotationSlider.setValue(30)
+ axisLabelRotationSlider.setMaximum(90)
+
+ modeGroup = QButtonGroup(self._barsWidget)
+ modeWeather = QRadioButton("Temperature Data", self._barsWidget)
+ modeWeather.setChecked(True)
+ modeCustomProxy = QRadioButton("Custom Proxy Data", self._barsWidget)
+ modeGroup.addButton(modeWeather)
+ modeGroup.addButton(modeCustomProxy)
+
+ vLayout.addWidget(QLabel("Rotate horizontally"))
+ vLayout.addWidget(rotationSliderX, 0, Qt.AlignTop)
+ vLayout.addWidget(QLabel("Rotate vertically"))
+ vLayout.addWidget(rotationSliderY, 0, Qt.AlignTop)
+ vLayout.addWidget(labelButton, 0, Qt.AlignTop)
+ vLayout.addWidget(cameraButton, 0, Qt.AlignTop)
+ vLayout.addWidget(zoomToSelectedButton, 0, Qt.AlignTop)
+ vLayout.addWidget(backgroundCheckBox)
+ vLayout.addWidget(gridCheckBox)
+ vLayout.addWidget(smoothCheckBox)
+ vLayout.addWidget(reflectionCheckBox)
+ vLayout.addWidget(seriesCheckBox)
+ vLayout.addWidget(reverseValueAxisCheckBox)
+ vLayout.addWidget(axisTitlesVisibleCB)
+ vLayout.addWidget(axisTitlesFixedCB)
+ vLayout.addWidget(QLabel("Show year"))
+ vLayout.addWidget(rangeList)
+ vLayout.addWidget(QLabel("Change bar style"))
+ vLayout.addWidget(barStyleList)
+ vLayout.addWidget(QLabel("Change selection mode"))
+ vLayout.addWidget(selectionModeList)
+ vLayout.addWidget(QLabel("Change theme"))
+ vLayout.addWidget(themeList)
+ vLayout.addWidget(QLabel("Adjust shadow quality"))
+ vLayout.addWidget(shadowQuality)
+ vLayout.addWidget(QLabel("Change font"))
+ vLayout.addWidget(fontList)
+ vLayout.addWidget(QLabel("Adjust font size"))
+ vLayout.addWidget(fontSizeSlider)
+ vLayout.addWidget(QLabel("Axis label rotation"))
+ vLayout.addWidget(axisLabelRotationSlider, 0, Qt.AlignTop)
+ vLayout.addWidget(modeWeather, 0, Qt.AlignTop)
+ vLayout.addWidget(modeCustomProxy, 1, Qt.AlignTop)
+
+ self._modifier = GraphModifier(self._barsGraph, self)
+
+ rotationSliderX.valueChanged.connect(self._modifier.rotateX)
+ rotationSliderY.valueChanged.connect(self._modifier.rotateY)
+
+ labelButton.clicked.connect(self._modifier.changeLabelBackground)
+ cameraButton.clicked.connect(self._modifier.changePresetCamera)
+ zoomToSelectedButton.clicked.connect(self._modifier.zoomToSelectedBar)
+
+ backgroundCheckBox.stateChanged.connect(self._modifier.setBackgroundEnabled)
+ gridCheckBox.stateChanged.connect(self._modifier.setGridEnabled)
+ smoothCheckBox.stateChanged.connect(self._modifier.setSmoothBars)
+ seriesCheckBox.stateChanged.connect(self._modifier.setSeriesVisibility)
+ reverseValueAxisCheckBox.stateChanged.connect(self._modifier.setReverseValueAxis)
+ reflectionCheckBox.stateChanged.connect(self._modifier.setReflection)
+
+ self._modifier.backgroundEnabledChanged.connect(backgroundCheckBox.setChecked)
+ self._modifier.gridEnabledChanged.connect(gridCheckBox.setChecked)
+
+ rangeList.currentIndexChanged.connect(self._modifier.changeRange)
+
+ barStyleList.currentIndexChanged.connect(self._modifier.changeStyle)
+
+ selectionModeList.currentIndexChanged.connect(self._modifier.changeSelectionMode)
+
+ themeList.currentIndexChanged.connect(self._modifier.changeTheme)
+
+ shadowQuality.currentIndexChanged.connect(self._modifier.changeShadowQuality)
+
+ self._modifier.shadowQualityChanged.connect(shadowQuality.setCurrentIndex)
+ self._barsGraph.shadowQualityChanged.connect(self._modifier.shadowQualityUpdatedByVisual)
+
+ fontSizeSlider.valueChanged.connect(self._modifier.changeFontSize)
+ fontList.currentFontChanged.connect(self._modifier.changeFont)
+
+ self._modifier.fontSizeChanged.connect(fontSizeSlider.setValue)
+ self._modifier.fontChanged.connect(fontList.setCurrentFont)
+
+ axisTitlesVisibleCB.stateChanged.connect(self._modifier.setAxisTitleVisibility)
+ axisTitlesFixedCB.stateChanged.connect(self._modifier.setAxisTitleFixed)
+ axisLabelRotationSlider.valueChanged.connect(self._modifier.changeLabelRotation)
+
+ modeWeather.toggled.connect(self._modifier.setDataModeToWeather)
+ modeCustomProxy.toggled.connect(self._modifier.setDataModeToCustom)
+ modeWeather.toggled.connect(seriesCheckBox.setEnabled)
+ modeWeather.toggled.connect(rangeList.setEnabled)
+ modeWeather.toggled.connect(axisTitlesVisibleCB.setEnabled)
+ modeWeather.toggled.connect(axisTitlesFixedCB.setEnabled)
+ modeWeather.toggled.connect(axisLabelRotationSlider.setEnabled)
+
+ def barsWidget(self):
+ return self._barsWidget
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from enum import Enum
+from math import sin, cos, degrees
+
+from PySide6.QtCore import Qt
+from PySide6.QtGraphs import (QAbstract3DGraph, Q3DInputHandler)
+
+
+class InputState(Enum):
+ StateNormal = 0
+ StateDraggingX = 1
+ StateDraggingZ = 2
+ StateDraggingY = 3
+
+
+class CustomInputHandler(Q3DInputHandler):
+
+ def __init__(self, graph, parent=None):
+ super().__init__(parent)
+ self._highlight = None
+ self._mousePressed = False
+ self._state = InputState.StateNormal
+ self._axisX = None
+ self._axisY = None
+ self._axisZ = None
+ self._speedModifier = 20.0
+ self._aspectRatio = 0.0
+ self._axisXMinValue = 0.0
+ self._axisXMaxValue = 0.0
+ self._axisXMinRange = 0.0
+ self._axisZMinValue = 0.0
+ self._axisZMaxValue = 0.0
+ self._axisZMinRange = 0.0
+ self._areaMinValue = 0.0
+ self._areaMaxValue = 0.0
+
+ # Connect to the item selection signal from graph
+ graph.selectedElementChanged.connect(self.handleElementSelected)
+
+ def setAspectRatio(self, ratio):
+ self._aspectRatio = ratio
+
+ def setHighlightSeries(self, series):
+ self._highlight = series
+
+ def setDragSpeedModifier(self, modifier):
+ self._speedModifier = modifier
+
+ def setLimits(self, min, max, minRange):
+ self._areaMinValue = min
+ self._areaMaxValue = max
+ self._axisXMinValue = self._areaMinValue
+ self._axisXMaxValue = self._areaMaxValue
+ self._axisZMinValue = self._areaMinValue
+ self._axisZMaxValue = self._areaMaxValue
+ self._axisXMinRange = minRange
+ self._axisZMinRange = minRange
+
+ def setAxes(self, axisX, axisY, axisZ):
+ self._axisX = axisX
+ self._axisY = axisY
+ self._axisZ = axisZ
+
+ def mousePressEvent(self, event, mousePos):
+ if Qt.LeftButton == event.button():
+ self._highlight.setVisible(False)
+ self._mousePressed = True
+ super().mousePressEvent(event, mousePos)
+
+ def wheelEvent(self, event):
+ delta = float(event.angleDelta().y())
+
+ self._axisXMinValue += delta
+ self._axisXMaxValue -= delta
+ self._axisZMinValue += delta
+ self._axisZMaxValue -= delta
+ self.checkConstraints()
+
+ y = (self._axisXMaxValue - self._axisXMinValue) * self._aspectRatio
+
+ self._axisX.setRange(self._axisXMinValue, self._axisXMaxValue)
+ self._axisY.setRange(100.0, y)
+ self._axisZ.setRange(self._axisZMinValue, self._axisZMaxValue)
+
+ def mouseMoveEvent(self, event, mousePos):
+ # Check if we're trying to drag axis label
+ if self._mousePressed and self._state != InputState.StateNormal:
+ self.setPreviousInputPos(self.inputPosition())
+ self.setInputPosition(mousePos)
+ self.handleAxisDragging()
+ else:
+ super().mouseMoveEvent(event, mousePos)
+
+ def mouseReleaseEvent(self, event, mousePos):
+ super().mouseReleaseEvent(event, mousePos)
+ self._mousePressed = False
+ self._state = InputState.StateNormal
+
+ def handleElementSelected(self, type):
+ if type == QAbstract3DGraph.ElementAxisXLabel:
+ self._state = InputState.StateDraggingX
+ elif type == QAbstract3DGraph.ElementAxisZLabel:
+ self._state = InputState.StateDraggingZ
+ else:
+ self._state = InputState.StateNormal
+
+ def handleAxisDragging(self):
+ distance = 0.0
+
+ # Get scene orientation from active camera
+ xRotation = self.scene().cameraXRotation()
+
+ # Calculate directional drag multipliers based on rotation
+ xMulX = cos(degrees(xRotation))
+ xMulY = sin(degrees(xRotation))
+ zMulX = xMulY
+ zMulY = xMulX
+
+ # Get the drag amount
+ move = self.inputPosition() - self.previousInputPos()
+
+ # Adjust axes
+ if self._state == InputState.StateDraggingX:
+ distance = (move.x() * xMulX - move.y() * xMulY) * self._speedModifier
+ self._axisXMinValue -= distance
+ self._axisXMaxValue -= distance
+ if self._axisXMinValue < self._areaMinValue:
+ dist = self._axisXMaxValue - self._axisXMinValue
+ self._axisXMinValue = self._areaMinValue
+ self._axisXMaxValue = self._axisXMinValue + dist
+
+ if self._axisXMaxValue > self._areaMaxValue:
+ dist = self._axisXMaxValue - self._axisXMinValue
+ self._axisXMaxValue = self._areaMaxValue
+ self._axisXMinValue = self._axisXMaxValue - dist
+
+ self._axisX.setRange(self._axisXMinValue, self._axisXMaxValue)
+ elif self._state == InputState.StateDraggingZ:
+ distance = (move.x() * zMulX + move.y() * zMulY) * self._speedModifier
+ self._axisZMinValue += distance
+ self._axisZMaxValue += distance
+ if self._axisZMinValue < self._areaMinValue:
+ dist = self._axisZMaxValue - self._axisZMinValue
+ self._axisZMinValue = self._areaMinValue
+ self._axisZMaxValue = self._axisZMinValue + dist
+
+ if self._axisZMaxValue > self._areaMaxValue:
+ dist = self._axisZMaxValue - self._axisZMinValue
+ self._axisZMaxValue = self._areaMaxValue
+ self._axisZMinValue = self._axisZMaxValue - dist
+
+ self._axisZ.setRange(self._axisZMinValue, self._axisZMaxValue)
+
+ def checkConstraints(self):
+ if self._axisXMinValue < self._areaMinValue:
+ self._axisXMinValue = self._areaMinValue
+ if self._axisXMaxValue > self._areaMaxValue:
+ self._axisXMaxValue = self._areaMaxValue
+ # Don't allow too much zoom in
+ range = self._axisXMaxValue - self._axisXMinValue
+ if range < self._axisXMinRange:
+ adjust = (self._axisXMinRange - range) / 2.0
+ self._axisXMinValue -= adjust
+ self._axisXMaxValue += adjust
+
+ if self._axisZMinValue < self._areaMinValue:
+ self._axisZMinValue = self._areaMinValue
+ if self._axisZMaxValue > self._areaMaxValue:
+ self._axisZMaxValue = self._areaMaxValue
+ # Don't allow too much zoom in
+ range = self._axisZMaxValue - self._axisZMinValue
+ if range < self._axisZMinRange:
+ adjust = (self._axisZMinRange - range) / 2.0
+ self._axisZMinValue -= adjust
+ self._axisZMaxValue += adjust
--- /dev/null
+License information regarding the data obtained from National Land Survey of
+Finland http://www.maanmittauslaitos.fi/en
+- topographic model from Elevation model 2 m (U4421B, U4421D, U4422A and
+ U4422C) 08/2014
+- map image extracted from Topographic map raster 1:50 000 (U442) 08/2014
+
+National Land Survey open data licence - version 1.0 - 1 May 2012
+
+1. General information
+
+The National Land Survey of Finland (hereinafter the Licensor), as the holder
+of the immaterial rights to the data, has granted on the terms mentioned below
+the right to use a copy (hereinafter data or dataset(s)) of the data (or a part
+of it).
+
+The Licensee is a natural or legal person who makes use of the data covered by
+this licence. The Licensee accepts the terms of this licence by receiving the
+dataset(s) covered by the licence.
+
+This Licence agreement does not create a co-operation or business relationship
+between the Licensee and the Licensor.
+
+2. Terms of the licence
+
+2.1. Right of use
+
+This licence grants a worldwide, free of charge and irrevocable parallel right
+of use to open data. According to the terms of the licence, data received by
+the Licensee can be freely:
+ - copied, distributed and published,
+ - modified and utilised commercially and non-commercially,
+ - inserted into other products and
+ - used as a part of a software application or service.
+
+2.2. Duties and responsibilities of the Licensee
+
+Through reasonable means suitable to the distribution medium or method which is
+used in conjunction with a product containing data or a service utilising data
+covered by this licence or while distributing data, the Licensee shall:
+ - mention the name of the Licensor, the name of the dataset(s) and the time
+ when the National Land Survey has delivered the dataset(s) (e.g.: contains
+ data from the National Land Survey of Finland Topographic Database 06/2012)
+ - provide a copy of this licence or a link to it, as well as
+ - require third parties to provide the same information when granting rights
+ to copies of dataset(s) or products and services containing such data and
+ - remove the name of the Licensor from the product or service, if required to
+ do so by the Licensor.
+
+The terms of this licence do not allow the Licensee to state in conjunction
+with the use of dataset(s) that the Licensor supports or recommends such use.
+
+2.3. Duties and responsibilities of the Licensor
+
+The Licensor shall ensure that
+ - the Licensor has the right to grant rights to the dataset(s) in accordance
+ with this licence.
+
+The data has been licensed "as is" and the Licensor
+ - shall not be held responsible for any errors or omissions in the data,
+ disclaims any warranty for the validity or up to date status of the data and
+ shall be free from liability for direct or consequential damages arising
+ from the use of data provided by the Licensor,
+ - and is not obligated to ensure the continuous availability of the data, nor
+ to announce in advance the interruption or cessation of availability, and
+ the Licensor shall be free from liability for direct or consequential
+ damages arising from any such interruption or cessation.
+
+3. Jurisdiction
+
+Finnish law shall apply to this licence.
+
+4. Changes to this licence
+
+The Licensor may at any time change the terms of the licence or apply a
+different licence to the data. The terms of this licence shall, however, still
+apply to such data that has been received prior to the change of the terms of
+the licence or the licence itself.
--- /dev/null
+# Rainfall per month from 2010 to 2022 in Northern Finland (Oulu)
+# Format: year, month, rainfall
+2010,1, 0,
+2010,2, 3.4,
+2010,3, 52,
+2010,4, 33.8,
+2010,5, 45.6,
+2010,6, 43.8,
+2010,7, 104.6,
+2010,8, 105.4,
+2010,9, 107.2,
+2010,10,38.6,
+2010,11,17.8,
+2010,12,0,
+2011,1, 8.2,
+2011,2, 1.6,
+2011,3, 27.4,
+2011,4, 15.8,
+2011,5, 57.6,
+2011,6, 85.2,
+2011,7, 127,
+2011,8, 72.2,
+2011,9, 82.2,
+2011,10,62.4,
+2011,11,31.6,
+2011,12,53.8,
+2012,1, 0,
+2012,2, 5,
+2012,3, 32.4,
+2012,4, 57.6,
+2012,5, 71.4,
+2012,6, 60.8,
+2012,7, 109,
+2012,8, 43.6,
+2012,9, 79.4,
+2012,10,117.2,
+2012,11,59,
+2012,12,0.2,
+2013,1, 28,
+2013,2, 19,
+2013,3, 0,
+2013,4, 37.6,
+2013,5, 44.2,
+2013,6, 104.8,
+2013,7, 84.2,
+2013,8, 57.2,
+2013,9, 37.2,
+2013,10,64.6,
+2013,11,77.8,
+2013,12,92.8,
+2014,1, 23.8,
+2014,2, 23.6,
+2014,3, 15.4,
+2014,4, 13.2,
+2014,5, 36.4,
+2014,6, 26.4,
+2014,7, 95.8,
+2014,8, 81.8,
+2014,9, 13.8,
+2014,10,94.6,
+2014,11,44.6,
+2014,12,31,
+2015,1, 37.4,
+2015,2, 21,
+2015,3, 42,
+2015,4, 8.8,
+2015,5, 82.4,
+2015,6, 150,
+2015,7, 56.8,
+2015,8, 67.2,
+2015,9, 131.2,
+2015,10,38.4,
+2015,11,83.4,
+2015,12,47.8,
+2016,1, 12.4,
+2016,2, 34.8,
+2016,3, 29,
+2016,4, 40.4,
+2016,5, 32.4,
+2016,6, 80.2,
+2016,7, 102.6,
+2016,8, 95.6,
+2016,9, 40.2,
+2016,10,7.8,
+2016,11,39.6,
+2016,12,8.8,
+2017,1, 9.4,
+2017,2, 6.6,
+2017,3, 29,
+2017,4, 46.2,
+2017,5, 43.2,
+2017,6, 25.2,
+2017,7, 72.4,
+2017,8, 58.8,
+2017,9, 68.8,
+2017,10,45.8,
+2017,11,36.8,
+2017,12,29.6,
+2018,1, 19.8,
+2018,2, 0.8,
+2018,3, 4,
+2018,4, 23.2,
+2018,5, 13.2,
+2018,6, 62.8,
+2018,7, 33,
+2018,8, 96.6,
+2018,9, 72.6,
+2018,10,48.8,
+2018,11,31.8,
+2018,12,12.8,
+2019,1, 0.2,
+2019,2, 24.8,
+2019,3, 32,
+2019,4, 8.8,
+2019,5, 71.4,
+2019,6, 65.8,
+2019,7, 17.6,
+2019,8, 90,
+2019,9, 50,
+2019,10,77,
+2019,11,27,
+2019,12,43.2,
+2020,1, 28.8,
+2020,2, 45,
+2020,3, 18.6,
+2020,4, 13,
+2020,5, 30.8,
+2020,6, 21.4,
+2020,7, 163.6,
+2020,8, 12,
+2020,9, 102.4,
+2020,10,133.2,
+2020,11,69.8,
+2020,12,40.6,
+2021,1, 0.4,
+2021,2, 21.6,
+2021,3, 24,
+2021,4, 51.4,
+2021,5, 76.4,
+2021,6, 29.2,
+2021,7, 36.4,
+2021,8, 116,
+2021,9, 72.4,
+2021,10,93.4,
+2021,11,21,
+2021,12,10.2,
+2022,1, 8.6,
+2022,2, 6.6,
+2022,3, 5.2,
+2022,4, 15.2,
+2022,5, 37.6,
+2022,6, 45,
+2022,7, 67.4,
+2022,8, 161.6,
+2022,9, 22.8,
+2022,10,75.2,
+2022,11,21.8,
+2022,12,0.2
--- /dev/null
+Widget Gallery
+==============
+
+
+Widget Gallery demonstrates all three graph types and some of their special
+features. The graphs have their own tabs in the application.
+
+
+.. image:: widgetgallery.webp
+ :width: 400
+ :alt: Widget Screenshot
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+
+from math import atan, degrees
+import numpy as np
+
+from PySide6.QtCore import QObject, QPropertyAnimation, Signal, Slot
+from PySide6.QtGui import QFont, QVector3D
+from PySide6.QtGraphs import (QAbstract3DGraph, QAbstract3DSeries,
+ QBarDataItem, QBar3DSeries, QCategory3DAxis,
+ QValue3DAxis, Q3DTheme)
+
+from rainfalldata import RainfallData
+
+# Set up data
+TEMP_OULU = np.array([
+ [-7.4, -2.4, 0.0, 3.0, 8.2, 11.6, 14.7, 15.4, 11.4, 4.2, 2.1, -2.3], # 2015
+ [-13.4, -3.9, -1.8, 3.1, 10.6, 13.7, 17.8, 13.6, 10.7, 3.5, -3.1, -4.2], # 2016
+ [-5.7, -6.7, -3.0, -0.1, 4.7, 12.4, 16.1, 14.1, 9.4, 3.0, -0.3, -3.2], # 2017
+ [-6.4, -11.9, -7.4, 1.9, 11.4, 12.4, 21.5, 16.1, 11.0, 4.4, 2.1, -4.1], # 2018
+ [-11.7, -6.1, -2.4, 3.9, 7.2, 14.5, 15.6, 14.4, 8.5, 2.0, -3.0, -1.5], # 2019
+ [-2.1, -3.4, -1.8, 0.6, 7.0, 17.1, 15.6, 15.4, 11.1, 5.6, 1.9, -1.7], # 2020
+ [-9.6, -11.6, -3.2, 2.4, 7.8, 17.3, 19.4, 14.2, 8.0, 5.2, -2.2, -8.6], # 2021
+ [-7.3, -6.4, -1.8, 1.3, 8.1, 15.5, 17.6, 17.6, 9.1, 5.4, -1.5, -4.4]], # 2022
+ np.float64)
+
+
+TEMP_HELSINKI = np.array([
+ [-2.0, -0.1, 1.8, 5.1, 9.7, 13.7, 16.3, 17.3, 12.7, 5.4, 4.6, 2.1], # 2015
+ [-10.3, -0.6, 0.0, 4.9, 14.3, 15.7, 17.7, 16.0, 12.7, 4.6, -1.0, -0.9], # 2016
+ [-2.9, -3.3, 0.7, 2.3, 9.9, 13.8, 16.1, 15.9, 11.4, 5.0, 2.7, 0.7], # 2017
+ [-2.2, -8.4, -4.7, 5.0, 15.3, 15.8, 21.2, 18.2, 13.3, 6.7, 2.8, -2.0], # 2018
+ [-6.2, -0.5, -0.3, 6.8, 10.6, 17.9, 17.5, 16.8, 11.3, 5.2, 1.8, 1.4], # 2019
+ [1.9, 0.5, 1.7, 4.5, 9.5, 18.4, 16.5, 16.8, 13.0, 8.2, 4.4, 0.9], # 2020
+ [-4.7, -8.1, -0.9, 4.5, 10.4, 19.2, 20.9, 15.4, 9.5, 8.0, 1.5, -6.7], # 2021
+ [-3.3, -2.2, -0.2, 3.3, 9.6, 16.9, 18.1, 18.9, 9.2, 7.6, 2.3, -3.4]], # 2022
+ np.float64)
+
+
+class GraphModifier(QObject):
+
+ shadowQualityChanged = Signal(int)
+ backgroundEnabledChanged = Signal(bool)
+ gridEnabledChanged = Signal(bool)
+ fontChanged = Signal(QFont)
+ fontSizeChanged = Signal(int)
+
+ def __init__(self, bargraph, parent):
+ super().__init__(parent)
+ self._graph = bargraph
+ self._temperatureAxis = QValue3DAxis()
+ self._yearAxis = QCategory3DAxis()
+ self._monthAxis = QCategory3DAxis()
+ self._primarySeries = QBar3DSeries()
+ self._secondarySeries = QBar3DSeries()
+ self._celsiusString = "°C"
+
+ self._xRotation = float(0)
+ self._yRotation = float(0)
+ self._fontSize = 30
+ self._segments = 4
+ self._subSegments = 3
+ self._minval = float(-20)
+ self._maxval = float(20)
+ self._barMesh = QAbstract3DSeries.Mesh.BevelBar
+ self._smooth = False
+ self._animationCameraX = QPropertyAnimation()
+ self._animationCameraY = QPropertyAnimation()
+ self._animationCameraZoom = QPropertyAnimation()
+ self._animationCameraTarget = QPropertyAnimation()
+ self._defaultAngleX = float(0)
+ self._defaultAngleY = float(0)
+ self._defaultZoom = float(0)
+ self._defaultTarget = []
+ self._customData = None
+
+ self._graph.setShadowQuality(QAbstract3DGraph.ShadowQuality.SoftMedium)
+ theme = self._graph.activeTheme()
+ theme.setBackgroundEnabled(False)
+ theme.setFont(QFont("Times New Roman", self._fontSize))
+ theme.setLabelBackgroundEnabled(True)
+ self._graph.setMultiSeriesUniform(True)
+
+ self._months = ["January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November",
+ "December"]
+ self._years = ["2015", "2016", "2017", "2018", "2019", "2020",
+ "2021", "2022"]
+
+ self._temperatureAxis.setTitle("Average temperature")
+ self._temperatureAxis.setSegmentCount(self._segments)
+ self._temperatureAxis.setSubSegmentCount(self._subSegments)
+ self._temperatureAxis.setRange(self._minval, self._maxval)
+ self._temperatureAxis.setLabelFormat("%.1f " + self._celsiusString)
+ self._temperatureAxis.setLabelAutoRotation(30.0)
+ self._temperatureAxis.setTitleVisible(True)
+
+ self._yearAxis.setTitle("Year")
+ self._yearAxis.setLabelAutoRotation(30.0)
+ self._yearAxis.setTitleVisible(True)
+ self._monthAxis.setTitle("Month")
+ self._monthAxis.setLabelAutoRotation(30.0)
+ self._monthAxis.setTitleVisible(True)
+
+ self._graph.setValueAxis(self._temperatureAxis)
+ self._graph.setRowAxis(self._yearAxis)
+ self._graph.setColumnAxis(self._monthAxis)
+
+ format = "Oulu - @colLabel @rowLabel: @valueLabel"
+ self._primarySeries.setItemLabelFormat(format)
+ self._primarySeries.setMesh(QAbstract3DSeries.Mesh.BevelBar)
+ self._primarySeries.setMeshSmooth(False)
+
+ format = "Helsinki - @colLabel @rowLabel: @valueLabel"
+ self._secondarySeries.setItemLabelFormat(format)
+ self._secondarySeries.setMesh(QAbstract3DSeries.Mesh.BevelBar)
+ self._secondarySeries.setMeshSmooth(False)
+ self._secondarySeries.setVisible(False)
+
+ self._graph.addSeries(self._primarySeries)
+ self._graph.addSeries(self._secondarySeries)
+
+ self.changePresetCamera()
+
+ self.resetTemperatureData()
+
+ # Set up property animations for zooming to the selected bar
+ self._defaultAngleX = self._graph.cameraXRotation()
+ self._defaultAngleY = self._graph.cameraYRotation()
+ self._defaultZoom = self._graph.cameraZoomLevel()
+ self._defaultTarget = self._graph.cameraTargetPosition()
+
+ self._animationCameraX.setTargetObject(self._graph)
+ self._animationCameraY.setTargetObject(self._graph)
+ self._animationCameraZoom.setTargetObject(self._graph)
+ self._animationCameraTarget.setTargetObject(self._graph)
+
+ self._animationCameraX.setPropertyName(b"cameraXRotation")
+ self._animationCameraY.setPropertyName(b"cameraYRotation")
+ self._animationCameraZoom.setPropertyName(b"cameraZoomLevel")
+ self._animationCameraTarget.setPropertyName(b"cameraTargetPosition")
+
+ duration = 1700
+ self._animationCameraX.setDuration(duration)
+ self._animationCameraY.setDuration(duration)
+ self._animationCameraZoom.setDuration(duration)
+ self._animationCameraTarget.setDuration(duration)
+
+ # The zoom always first zooms out above the graph and then zooms in
+ zoomOutFraction = 0.3
+ self._animationCameraX.setKeyValueAt(zoomOutFraction, 0.0)
+ self._animationCameraY.setKeyValueAt(zoomOutFraction, 90.0)
+ self._animationCameraZoom.setKeyValueAt(zoomOutFraction, 50.0)
+ self._animationCameraTarget.setKeyValueAt(zoomOutFraction,
+ QVector3D(0, 0, 0))
+ self._customData = RainfallData()
+
+ def resetTemperatureData(self):
+ # Create data arrays
+ dataSet = []
+ dataSet2 = []
+
+ for year in range(0, len(self._years)):
+ # Create a data row
+ dataRow = []
+ dataRow2 = []
+ for month in range(0, len(self._months)):
+ # Add data to the row
+ item = QBarDataItem()
+ item.setValue(TEMP_OULU[year][month])
+ dataRow.append(item)
+ item = QBarDataItem()
+ item.setValue(TEMP_HELSINKI[year][month])
+ dataRow2.append(item)
+
+ # Add the row to the set
+ dataSet.append(dataRow)
+ dataSet2.append(dataRow2)
+
+ # Add data to the data proxy (the data proxy assumes ownership of it)
+ self._primarySeries.dataProxy().resetArray(dataSet, self._years, self._months)
+ self._secondarySeries.dataProxy().resetArray(dataSet2, self._years, self._months)
+
+ @Slot(int)
+ def changeRange(self, range):
+ if range >= len(self._years):
+ self._yearAxis.setRange(0, len(self._years) - 1)
+ else:
+ self._yearAxis.setRange(range, range)
+
+ @Slot(int)
+ def changeStyle(self, style):
+ comboBox = self.sender()
+ if comboBox:
+ self._barMesh = comboBox.itemData(style)
+ self._primarySeries.setMesh(self._barMesh)
+ self._secondarySeries.setMesh(self._barMesh)
+ self._customData.customSeries().setMesh(self._barMesh)
+
+ def changePresetCamera(self):
+ self._animationCameraX.stop()
+ self._animationCameraY.stop()
+ self._animationCameraZoom.stop()
+ self._animationCameraTarget.stop()
+
+ # Restore camera target in case animation has changed it
+ self._graph.setCameraTargetPosition(QVector3D(0.0, 0.0, 0.0))
+
+ self._preset = QAbstract3DGraph.CameraPreset.Front.value
+
+ self._graph.setCameraPreset(QAbstract3DGraph.CameraPreset(self._preset))
+
+ self._preset += 1
+ if self._preset > QAbstract3DGraph.CameraPreset.DirectlyBelow.value:
+ self._preset = QAbstract3DGraph.CameraPreset.FrontLow.value
+
+ @Slot(int)
+ def changeTheme(self, theme):
+ currentTheme = self._graph.activeTheme()
+ currentTheme.setType(Q3DTheme.Theme(theme))
+ self.backgroundEnabledChanged.emit(currentTheme.isBackgroundEnabled())
+ self.gridEnabledChanged.emit(currentTheme.isGridEnabled())
+ self.fontChanged.emit(currentTheme.font())
+ self.fontSizeChanged.emit(currentTheme.font().pointSize())
+
+ def changeLabelBackground(self):
+ theme = self._graph.activeTheme()
+ theme.setLabelBackgroundEnabled(not theme.isLabelBackgroundEnabled())
+
+ @Slot(int)
+ def changeSelectionMode(self, selectionMode):
+ comboBox = self.sender()
+ if comboBox:
+ flags = comboBox.itemData(selectionMode)
+ self._graph.setSelectionMode(QAbstract3DGraph.SelectionFlags(flags))
+
+ def changeFont(self, font):
+ newFont = font
+ self._graph.activeTheme().setFont(newFont)
+
+ def changeFontSize(self, fontsize):
+ self._fontSize = fontsize
+ font = self._graph.activeTheme().font()
+ font.setPointSize(self._fontSize)
+ self._graph.activeTheme().setFont(font)
+
+ @Slot(QAbstract3DGraph.ShadowQuality)
+ def shadowQualityUpdatedByVisual(self, sq):
+ # Updates the UI component to show correct shadow quality
+ self.shadowQualityChanged.emit(sq.value)
+
+ @Slot(int)
+ def changeLabelRotation(self, rotation):
+ self._temperatureAxis.setLabelAutoRotation(float(rotation))
+ self._monthAxis.setLabelAutoRotation(float(rotation))
+ self._yearAxis.setLabelAutoRotation(float(rotation))
+
+ @Slot(bool)
+ def setAxisTitleVisibility(self, enabled):
+ self._temperatureAxis.setTitleVisible(enabled)
+ self._monthAxis.setTitleVisible(enabled)
+ self._yearAxis.setTitleVisible(enabled)
+
+ @Slot(bool)
+ def setAxisTitleFixed(self, enabled):
+ self._temperatureAxis.setTitleFixed(enabled)
+ self._monthAxis.setTitleFixed(enabled)
+ self._yearAxis.setTitleFixed(enabled)
+
+ @Slot()
+ def zoomToSelectedBar(self):
+ self._animationCameraX.stop()
+ self._animationCameraY.stop()
+ self._animationCameraZoom.stop()
+ self._animationCameraTarget.stop()
+
+ currentX = self._graph.cameraXRotation()
+ currentY = self._graph.cameraYRotation()
+ currentZoom = self._graph.cameraZoomLevel()
+ currentTarget = self._graph.cameraTargetPosition()
+
+ self._animationCameraX.setStartValue(currentX)
+ self._animationCameraY.setStartValue(currentY)
+ self._animationCameraZoom.setStartValue(currentZoom)
+ self._animationCameraTarget.setStartValue(currentTarget)
+
+ selectedBar = (self._graph.selectedSeries().selectedBar()
+ if self._graph.selectedSeries()
+ else QBar3DSeries.invalidSelectionPosition())
+
+ if selectedBar != QBar3DSeries.invalidSelectionPosition():
+ # Normalize selected bar position within axis range to determine
+ # target coordinates
+ endTarget = QVector3D()
+ xMin = self._graph.columnAxis().min()
+ xRange = self._graph.columnAxis().max() - xMin
+ zMin = self._graph.rowAxis().min()
+ zRange = self._graph.rowAxis().max() - zMin
+ endTarget.setX((selectedBar.y() - xMin) / xRange * 2.0 - 1.0)
+ endTarget.setZ((selectedBar.x() - zMin) / zRange * 2.0 - 1.0)
+
+ # Rotate the camera so that it always points approximately to the
+ # graph center
+ endAngleX = 90.0 - degrees(atan(float(endTarget.z() / endTarget.x())))
+ if endTarget.x() > 0.0:
+ endAngleX -= 180.0
+ proxy = self._graph.selectedSeries().dataProxy()
+ barValue = proxy.itemAt(selectedBar.x(), selectedBar.y()).value()
+ endAngleY = 30.0 if barValue >= 0.0 else -30.0
+ if self._graph.valueAxis().reversed():
+ endAngleY *= -1.0
+
+ self._animationCameraX.setEndValue(float(endAngleX))
+ self._animationCameraY.setEndValue(endAngleY)
+ self._animationCameraZoom.setEndValue(250)
+ self._animationCameraTarget.setEndValue(endTarget)
+ else:
+ # No selected bar, so return to the default view
+ self._animationCameraX.setEndValue(self._defaultAngleX)
+ self._animationCameraY.setEndValue(self._defaultAngleY)
+ self._animationCameraZoom.setEndValue(self._defaultZoom)
+ self._animationCameraTarget.setEndValue(self._defaultTarget)
+
+ self._animationCameraX.start()
+ self._animationCameraY.start()
+ self._animationCameraZoom.start()
+ self._animationCameraTarget.start()
+
+ @Slot(bool)
+ def setDataModeToWeather(self, enabled):
+ if enabled:
+ self.changeDataMode(False)
+
+ @Slot(bool)
+ def setDataModeToCustom(self, enabled):
+ if enabled:
+ self.changeDataMode(True)
+
+ def changeShadowQuality(self, quality):
+ sq = QAbstract3DGraph.ShadowQuality(quality)
+ self._graph.setShadowQuality(sq)
+ self.shadowQualityChanged.emit(quality)
+
+ def rotateX(self, rotation):
+ self._xRotation = rotation
+ camera = self._graph.scene().activeCamera()
+ camera.setCameraPosition(self._xRotation, self._yRotation)
+
+ def rotateY(self, rotation):
+ self._yRotation = rotation
+ camera = self._graph.scene().activeCamera()
+ camera.setCameraPosition(self._xRotation, self._yRotation)
+
+ def setBackgroundEnabled(self, enabled):
+ self._graph.activeTheme().setBackgroundEnabled(bool(enabled))
+
+ def setGridEnabled(self, enabled):
+ self._graph.activeTheme().setGridEnabled(bool(enabled))
+
+ def setSmoothBars(self, smooth):
+ self._smooth = bool(smooth)
+ self._primarySeries.setMeshSmooth(self._smooth)
+ self._secondarySeries.setMeshSmooth(self._smooth)
+ self._customData.customSeries().setMeshSmooth(self._smooth)
+
+ def setSeriesVisibility(self, enabled):
+ self._secondarySeries.setVisible(bool(enabled))
+
+ def setReverseValueAxis(self, enabled):
+ self._graph.valueAxis().setReversed(enabled)
+
+ def setReflection(self, enabled):
+ self._graph.setReflection(enabled)
+
+ def changeDataMode(self, customData):
+ # Change between weather data and data from custom proxy
+ if customData:
+ self._graph.removeSeries(self._primarySeries)
+ self._graph.removeSeries(self._secondarySeries)
+ self._graph.addSeries(self._customData.customSeries())
+ self._graph.setValueAxis(self._customData.valueAxis())
+ self._graph.setRowAxis(self._customData.rowAxis())
+ self._graph.setColumnAxis(self._customData.colAxis())
+ else:
+ self._graph.removeSeries(self._customData.customSeries())
+ self._graph.addSeries(self._primarySeries)
+ self._graph.addSeries(self._secondarySeries)
+ self._graph.setValueAxis(self._temperatureAxis)
+ self._graph.setRowAxis(self._yearAxis)
+ self._graph.setColumnAxis(self._monthAxis)
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import QPoint, Qt, Slot
+from PySide6.QtGui import QLinearGradient, QVector3D
+from PySide6.QtGraphs import (QSurface3DSeries, QSurfaceDataItem, Q3DTheme)
+
+
+DARK_RED_POS = 1.0
+RED_POS = 0.8
+YELLOW_POS = 0.6
+GREEN_POS = 0.4
+DARK_GREEN_POS = 0.2
+
+
+class HighlightSeries(QSurface3DSeries):
+
+ def __init__(self):
+ super().__init__()
+ self._width = 100
+ self._height = 100
+ self._srcWidth = 0
+ self._srcHeight = 0
+ self._position = {}
+ self._topographicSeries = None
+ self._minHeight = 0.0
+ self.setDrawMode(QSurface3DSeries.DrawSurface)
+ self.setFlatShadingEnabled(True)
+ self.setVisible(False)
+
+ def setTopographicSeries(self, series):
+ self._topographicSeries = series
+ array = self._topographicSeries.dataProxy().array()
+ self._srcWidth = len(array[0])
+ self._srcHeight = len(array)
+ self._topographicSeries.selectedPointChanged.connect(self.handlePositionChange)
+
+ def setMinHeight(self, height):
+ self. m_minHeight = height
+
+ @Slot(QPoint)
+ def handlePositionChange(self, position):
+ self._position = position
+
+ if position == self.invalidSelectionPosition():
+ self.setVisible(False)
+ return
+
+ halfWidth = self._width / 2
+ halfHeight = self._height / 2
+
+ startX = position.y() - halfWidth
+ if startX < 0:
+ startX = 0
+ endX = position.y() + halfWidth
+ if endX > (self._srcWidth - 1):
+ endX = self._srcWidth - 1
+ startZ = position.x() - halfHeight
+ if startZ < 0:
+ startZ = 0
+ endZ = position.x() + halfHeight
+ if endZ > (self._srcHeight - 1):
+ endZ = self._srcHeight - 1
+
+ srcProxy = self._topographicSeries.dataProxy()
+ srcArray = srcProxy.array()
+
+ dataArray = []
+ for i in range(int(startZ), int(endZ)):
+ newRow = []
+ srcRow = srcArray[i]
+ for j in range(startX, endX):
+ pos = srcRow.at(j).position()
+ pos.setY(pos.y() + 0.1)
+ item = QSurfaceDataItem(QVector3D(pos))
+ newRow.append(item)
+ dataArray.append(newRow)
+ self.dataProxy().resetArray(dataArray)
+ self.setVisible(True)
+
+ @Slot(float)
+ def handleGradientChange(self, value):
+ ratio = self._minHeight / value
+
+ gr = QLinearGradient()
+ gr.setColorAt(0.0, Qt.black)
+ gr.setColorAt(DARK_GREEN_POS * ratio, Qt.darkGreen)
+ gr.setColorAt(GREEN_POS * ratio, Qt.green)
+ gr.setColorAt(YELLOW_POS * ratio, Qt.yellow)
+ gr.setColorAt(RED_POS * ratio, Qt.red)
+ gr.setColorAt(DARK_RED_POS * ratio, Qt.darkRed)
+
+ self.setBaseGradient(gr)
+ self.setColorStyle(Q3DTheme.ColorStyle.RangeGradient)
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+"""PySide6 port of the Qt Graphs widgetgallery example from Qt v6.x"""
+
+import sys
+
+from PySide6.QtCore import QSize
+from PySide6.QtWidgets import QApplication, QTabWidget
+
+from bargraph import BarGraph
+from scattergraph import ScatterGraph
+from surfacegraph import SurfaceGraph
+
+
+if __name__ == "__main__":
+ app = QApplication(sys.argv)
+
+ # Create a tab widget for creating own tabs for Q3DBars, Q3DScatter, and Q3DSurface
+ tabWidget = QTabWidget()
+ tabWidget.setWindowTitle("Widget Gallery")
+
+ screen_size = tabWidget.screen().size()
+ minimum_graph_size = QSize(screen_size.width() / 2, screen_size.height() / 1.75)
+
+ # Create bar graph
+ bars = BarGraph(minimum_graph_size, screen_size)
+ # Create scatter graph
+ scatter = ScatterGraph(minimum_graph_size, screen_size)
+ # Create surface graph
+ surface = SurfaceGraph(minimum_graph_size, screen_size)
+
+ # Add bars widget
+ tabWidget.addTab(bars.barsWidget(), "Bar Graph")
+ # Add scatter widget
+ tabWidget.addTab(scatter.scatterWidget(), "Scatter Graph")
+ # Add surface widget
+ tabWidget.addTab(surface.surfaceWidget(), "Surface Graph")
+
+ tabWidget.show()
+ sys.exit(app.exec())
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import sys
+
+from pathlib import Path
+
+from PySide6.QtCore import QFile, QIODevice, QObject
+from PySide6.QtGraphs import (QBar3DSeries, QCategory3DAxis, QValue3DAxis)
+
+from variantbardataproxy import VariantBarDataProxy
+from variantbardatamapping import VariantBarDataMapping
+from variantdataset import VariantDataSet
+
+
+MONTHS = ["January", "February", "March", "April",
+ "May", "June", "July", "August", "September", "October",
+ "November", "December"]
+
+
+class RainfallData(QObject):
+
+ def __init__(self):
+ super().__init__()
+ self._columnCount = 0
+ self._rowCount = 0
+ self._years = []
+ self._numericMonths = []
+ self._proxy = VariantBarDataProxy()
+ self._mapping = None
+ self._dataSet = None
+ self._series = QBar3DSeries()
+ self._valueAxis = QValue3DAxis()
+ self._rowAxis = QCategory3DAxis()
+ self._colAxis = QCategory3DAxis()
+
+ # In data file the months are in numeric format, so create custom list
+ for i in range(1, 13):
+ self._numericMonths.append(str(i))
+
+ self._columnCount = len(self._numericMonths)
+
+ self.updateYearsList(2010, 2022)
+
+ # Create proxy and series
+ self._proxy = VariantBarDataProxy()
+ self._series = QBar3DSeries(self._proxy)
+
+ self._series.setItemLabelFormat("%.1f mm")
+
+ # Create the axes
+ self._rowAxis = QCategory3DAxis(self)
+ self._colAxis = QCategory3DAxis(self)
+ self._valueAxis = QValue3DAxis(self)
+ self._rowAxis.setAutoAdjustRange(True)
+ self._colAxis.setAutoAdjustRange(True)
+ self._valueAxis.setAutoAdjustRange(True)
+
+ # Set axis labels and titles
+ self._rowAxis.setTitle("Year")
+ self._colAxis.setTitle("Month")
+ self._valueAxis.setTitle("rainfall (mm)")
+ self._valueAxis.setSegmentCount(5)
+ self._rowAxis.setLabels(self._years)
+ self._colAxis.setLabels(MONTHS)
+ self._rowAxis.setTitleVisible(True)
+ self._colAxis.setTitleVisible(True)
+ self._valueAxis.setTitleVisible(True)
+
+ self.addDataSet()
+
+ def customSeries(self):
+ return self._series
+
+ def valueAxis(self):
+ return self._valueAxis
+
+ def rowAxis(self):
+ return self._rowAxis
+
+ def colAxis(self):
+ return self._colAxis
+
+ def updateYearsList(self, start, end):
+ self._years.clear()
+ for i in range(start, end + 1):
+ self._years.append(str(i))
+ self._rowCount = len(self._years)
+
+ def addDataSet(self):
+ # Create a new variant data set and data item list
+ self._dataSet = VariantDataSet()
+ itemList = []
+
+ # Read data from a data file into the data item list
+ file_path = Path(__file__).resolve().parent / "data" / "raindata.txt"
+ dataFile = QFile(file_path)
+ if dataFile.open(QIODevice.ReadOnly | QIODevice.Text):
+ data = dataFile.readAll().data().decode("utf8")
+ for line in data.split("\n"):
+ if line and not line.startswith("#"): # Ignore comments
+ tokens = line.split(",")
+ # Each line has three data items: Year, month, and
+ # rainfall value
+ if len(tokens) >= 3:
+ # Store year and month as strings, and rainfall value
+ # as double into a variant data item and add the item to
+ # the item list.
+ newItem = []
+ newItem.append(tokens[0].strip())
+ newItem.append(tokens[1].strip())
+ newItem.append(float(tokens[2].strip()))
+ itemList.append(newItem)
+ else:
+ print("Unable to open data file:", dataFile.fileName(),
+ file=sys.stderr)
+
+ # Add items to the data set and set it to the proxy
+ self._dataSet.addItems(itemList)
+ self._proxy.setDataSet(self._dataSet)
+
+ # Create new mapping for the data and set it to the proxy
+ self._mapping = VariantBarDataMapping(0, 1, 2,
+ self._years, self._numericMonths)
+ self._proxy.setMapping(self._mapping)
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from math import cos, degrees, sqrt
+
+from PySide6.QtCore import QObject, Signal, Slot, Qt
+from PySide6.QtGui import QVector3D
+from PySide6.QtGraphs import (QAbstract3DGraph, QAbstract3DSeries,
+ QScatterDataItem, QScatterDataProxy,
+ QScatter3DSeries, Q3DTheme)
+
+from axesinputhandler import AxesInputHandler
+
+
+NUMBER_OF_ITEMS = 10000
+CURVE_DIVIDER = 7.5
+LOWER_NUMBER_OF_ITEMS = 900
+LOWER_CURVE_DIVIDER = 0.75
+
+
+class ScatterDataModifier(QObject):
+
+ backgroundEnabledChanged = Signal(bool)
+ gridEnabledChanged = Signal(bool)
+ shadowQualityChanged = Signal(int)
+
+ def __init__(self, scatter, parent):
+ super().__init__(parent)
+
+ self._graph = scatter
+
+ self._style = QAbstract3DSeries.Mesh.Sphere
+ self._smooth = True
+ self._inputHandler = AxesInputHandler(scatter)
+ self._autoAdjust = True
+ self._itemCount = LOWER_NUMBER_OF_ITEMS
+ self._CURVE_DIVIDER = LOWER_CURVE_DIVIDER
+ self._inputHandler = AxesInputHandler(scatter)
+
+ self._graph.activeTheme().setType(Q3DTheme.Theme.StoneMoss)
+ self._graph.setShadowQuality(QAbstract3DGraph.ShadowQuality.SoftHigh)
+ self._graph.setCameraPreset(QAbstract3DGraph.CameraPreset.Front)
+ self._graph.setCameraZoomLevel(80.0)
+
+ self._proxy = QScatterDataProxy()
+ self._series = QScatter3DSeries(self._proxy)
+ self._series.setItemLabelFormat("@xTitle: @xLabel @yTitle: @yLabel @zTitle: @zLabel")
+ self._series.setMeshSmooth(self._smooth)
+ self._graph.addSeries(self._series)
+ self._preset = QAbstract3DGraph.CameraPreset.FrontLow.value
+
+ # Give ownership of the handler to the graph and make it the active
+ # handler
+ self._graph.setActiveInputHandler(self._inputHandler)
+
+ # Give our axes to the input handler
+ self._inputHandler.setAxes(self._graph.axisX(), self._graph.axisZ(),
+ self._graph.axisY())
+
+ self.addData()
+
+ def addData(self):
+ # Configure the axes according to the data
+ self._graph.axisX().setTitle("X")
+ self._graph.axisY().setTitle("Y")
+ self._graph.axisZ().setTitle("Z")
+
+ dataArray = []
+ limit = int(sqrt(self._itemCount) / 2.0)
+ for i in range(-limit, limit):
+ for j in range(-limit, limit):
+ x = float(i) + 0.5
+ y = cos(degrees(float(i * j) / self._CURVE_DIVIDER))
+ z = float(j) + 0.5
+ dataArray.append(QScatterDataItem(QVector3D(x, y, z)))
+
+ self._graph.seriesList()[0].dataProxy().resetArray(dataArray)
+
+ @Slot(int)
+ def changeStyle(self, style):
+ comboBox = self.sender()
+ if comboBox:
+ self._style = comboBox.itemData(style)
+ if self._graph.seriesList():
+ self._graph.seriesList()[0].setMesh(self._style)
+
+ @Slot(int)
+ def setSmoothDots(self, smooth):
+ self._smooth = smooth == Qt.Checked.value
+ series = self._graph.seriesList()[0]
+ series.setMeshSmooth(self._smooth)
+
+ @Slot(int)
+ def changeTheme(self, theme):
+ currentTheme = self._graph.activeTheme()
+ currentTheme.setType(Q3DTheme.Theme(theme))
+ self.backgroundEnabledChanged.emit(currentTheme.isBackgroundEnabled())
+ self.gridEnabledChanged.emit(currentTheme.isGridEnabled())
+
+ @Slot()
+ def changePresetCamera(self):
+ camera = self._graph.scene().activeCamera()
+ camera.setCameraPreset(QAbstract3DGraph.CameraPreset(self._preset))
+
+ self._preset += 1
+ if self._preset > QAbstract3DGraph.CameraPreset.DirectlyBelow.value:
+ self._preset = QAbstract3DGraph.CameraPreset.FrontLow.value
+
+ @Slot(QAbstract3DGraph.ShadowQuality)
+ def shadowQualityUpdatedByVisual(self, sq):
+ self.shadowQualityChanged.emit(sq.value)
+
+ @Slot(int)
+ def changeShadowQuality(self, quality):
+ sq = QAbstract3DGraph.ShadowQuality(quality)
+ self._graph.setShadowQuality(sq)
+
+ @Slot(int)
+ def setBackgroundEnabled(self, enabled):
+ self._graph.activeTheme().setBackgroundEnabled(enabled == Qt.Checked.value)
+
+ @Slot(int)
+ def setGridEnabled(self, enabled):
+ self._graph.activeTheme().setGridEnabled(enabled == Qt.Checked.value)
+
+ @Slot()
+ def toggleItemCount(self):
+ if self._itemCount == NUMBER_OF_ITEMS:
+ self._itemCount = LOWER_NUMBER_OF_ITEMS
+ self._CURVE_DIVIDER = LOWER_CURVE_DIVIDER
+ else:
+ self._itemCount = NUMBER_OF_ITEMS
+ self._CURVE_DIVIDER = CURVE_DIVIDER
+
+ self._graph.seriesList()[0].dataProxy().resetArray([])
+ self.addData()
+
+ @Slot()
+ def toggleRanges(self):
+ if not self._autoAdjust:
+ self._graph.axisX().setAutoAdjustRange(True)
+ self._graph.axisZ().setAutoAdjustRange(True)
+ self._inputHandler.setDragSpeedModifier(1.5)
+ self._autoAdjust = True
+ else:
+ self._graph.axisX().setRange(-10.0, 10.0)
+ self._graph.axisZ().setRange(-10.0, 10.0)
+ self._inputHandler.setDragSpeedModifier(15.0)
+ self._autoAdjust = False
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import QObject, QSize, Qt
+from PySide6.QtWidgets import (QCheckBox, QComboBox, QCommandLinkButton,
+ QLabel, QHBoxLayout, QSizePolicy,
+ QVBoxLayout, QWidget, )
+from PySide6.QtQuickWidgets import QQuickWidget
+from PySide6.QtGraphs import (QAbstract3DSeries, Q3DScatter)
+
+from scatterdatamodifier import ScatterDataModifier
+
+
+class ScatterGraph(QObject):
+
+ def __init__(self, minimum_graph_size, maximum_graph_size):
+ super().__init__()
+ self._scatterGraph = Q3DScatter()
+ self._scatterWidget = QWidget()
+ hLayout = QHBoxLayout(self._scatterWidget)
+ self._scatterGraph.setMinimumSize(minimum_graph_size)
+ self._scatterGraph.setMaximumSize(maximum_graph_size)
+ self._scatterGraph.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ self._scatterGraph.setFocusPolicy(Qt.StrongFocus)
+ self._scatterGraph.setResizeMode(QQuickWidget.SizeRootObjectToView)
+ hLayout.addWidget(self._scatterGraph, 1)
+
+ vLayout = QVBoxLayout()
+ hLayout.addLayout(vLayout)
+
+ cameraButton = QCommandLinkButton(self._scatterWidget)
+ cameraButton.setText("Change camera preset")
+ cameraButton.setDescription("Switch between a number of preset camera positions")
+ cameraButton.setIconSize(QSize(0, 0))
+
+ itemCountButton = QCommandLinkButton(self._scatterWidget)
+ itemCountButton.setText("Toggle item count")
+ itemCountButton.setDescription("Switch between 900 and 10000 data points")
+ itemCountButton.setIconSize(QSize(0, 0))
+
+ rangeButton = QCommandLinkButton(self._scatterWidget)
+ rangeButton.setText("Toggle axis ranges")
+ rangeButton.setDescription("Switch between automatic axis ranges and preset ranges")
+ rangeButton.setIconSize(QSize(0, 0))
+
+ backgroundCheckBox = QCheckBox(self._scatterWidget)
+ backgroundCheckBox.setText("Show background")
+ backgroundCheckBox.setChecked(True)
+
+ gridCheckBox = QCheckBox(self._scatterWidget)
+ gridCheckBox.setText("Show grid")
+ gridCheckBox.setChecked(True)
+
+ smoothCheckBox = QCheckBox(self._scatterWidget)
+ smoothCheckBox.setText("Smooth dots")
+ smoothCheckBox.setChecked(True)
+
+ itemStyleList = QComboBox(self._scatterWidget)
+ itemStyleList.addItem("Sphere", QAbstract3DSeries.Mesh.Sphere)
+ itemStyleList.addItem("Cube", QAbstract3DSeries.Mesh.Cube)
+ itemStyleList.addItem("Minimal", QAbstract3DSeries.Mesh.Minimal)
+ itemStyleList.addItem("Point", QAbstract3DSeries.Mesh.Point)
+ itemStyleList.setCurrentIndex(0)
+
+ themeList = QComboBox(self._scatterWidget)
+ themeList.addItem("Qt")
+ themeList.addItem("Primary Colors")
+ themeList.addItem("Digia")
+ themeList.addItem("Stone Moss")
+ themeList.addItem("Army Blue")
+ themeList.addItem("Retro")
+ themeList.addItem("Ebony")
+ themeList.addItem("Isabelle")
+ themeList.setCurrentIndex(3)
+
+ shadowQuality = QComboBox(self._scatterWidget)
+ shadowQuality.addItem("None")
+ shadowQuality.addItem("Low")
+ shadowQuality.addItem("Medium")
+ shadowQuality.addItem("High")
+ shadowQuality.addItem("Low Soft")
+ shadowQuality.addItem("Medium Soft")
+ shadowQuality.addItem("High Soft")
+ shadowQuality.setCurrentIndex(6)
+
+ vLayout.addWidget(cameraButton)
+ vLayout.addWidget(itemCountButton)
+ vLayout.addWidget(rangeButton)
+ vLayout.addWidget(backgroundCheckBox)
+ vLayout.addWidget(gridCheckBox)
+ vLayout.addWidget(smoothCheckBox)
+ vLayout.addWidget(QLabel("Change dot style"))
+ vLayout.addWidget(itemStyleList)
+ vLayout.addWidget(QLabel("Change theme"))
+ vLayout.addWidget(themeList)
+ vLayout.addWidget(QLabel("Adjust shadow quality"))
+ vLayout.addWidget(shadowQuality, 1, Qt.AlignTop)
+
+ self._modifier = ScatterDataModifier(self._scatterGraph, self)
+
+ cameraButton.clicked.connect(self._modifier.changePresetCamera)
+ itemCountButton.clicked.connect(self._modifier.toggleItemCount)
+ rangeButton.clicked.connect(self._modifier.toggleRanges)
+
+ backgroundCheckBox.stateChanged.connect(self._modifier.setBackgroundEnabled)
+ gridCheckBox.stateChanged.connect(self._modifier.setGridEnabled)
+ smoothCheckBox.stateChanged.connect(self._modifier.setSmoothDots)
+
+ self._modifier.backgroundEnabledChanged.connect(backgroundCheckBox.setChecked)
+ self._modifier.gridEnabledChanged.connect(gridCheckBox.setChecked)
+ itemStyleList.currentIndexChanged.connect(self._modifier.changeStyle)
+
+ themeList.currentIndexChanged.connect(self._modifier.changeTheme)
+
+ shadowQuality.currentIndexChanged.connect(self._modifier.changeShadowQuality)
+
+ self._modifier.shadowQualityChanged.connect(shadowQuality.setCurrentIndex)
+ self._scatterGraph.shadowQualityChanged.connect(self._modifier.shadowQualityUpdatedByVisual)
+
+ def scatterWidget(self):
+ return self._scatterWidget
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from surfacegraphmodifier import SurfaceGraphModifier
+
+from PySide6.QtCore import QObject, Qt
+from PySide6.QtGui import QBrush, QIcon, QLinearGradient, QPainter, QPixmap
+from PySide6.QtWidgets import (QGroupBox, QCheckBox, QLabel, QHBoxLayout,
+ QPushButton, QRadioButton, QSizePolicy, QSlider,
+ QVBoxLayout, QWidget)
+from PySide6.QtQuickWidgets import QQuickWidget
+from PySide6.QtGraphs import Q3DSurface
+
+
+def gradientBtoYPB_Pixmap():
+ grBtoY = QLinearGradient(0, 0, 1, 100)
+ grBtoY.setColorAt(1.0, Qt.black)
+ grBtoY.setColorAt(0.67, Qt.blue)
+ grBtoY.setColorAt(0.33, Qt.red)
+ grBtoY.setColorAt(0.0, Qt.yellow)
+ pm = QPixmap(24, 100)
+ with QPainter(pm) as pmp:
+ pmp.setBrush(QBrush(grBtoY))
+ pmp.setPen(Qt.NoPen)
+ pmp.drawRect(0, 0, 24, 100)
+ return pm
+
+
+def gradientGtoRPB_Pixmap():
+ grGtoR = QLinearGradient(0, 0, 1, 100)
+ grGtoR.setColorAt(1.0, Qt.darkGreen)
+ grGtoR.setColorAt(0.5, Qt.yellow)
+ grGtoR.setColorAt(0.2, Qt.red)
+ grGtoR.setColorAt(0.0, Qt.darkRed)
+ pm = QPixmap(24, 100)
+ with QPainter(pm) as pmp:
+ pmp.setBrush(QBrush(grGtoR))
+ pmp.setPen(Qt.NoPen)
+ pmp.drawRect(0, 0, 24, 100)
+ return pm
+
+
+def highlightPixmap():
+ HEIGHT = 400
+ WIDTH = 110
+ BORDER = 10
+ gr = QLinearGradient(0, 0, 1, HEIGHT - 2 * BORDER)
+ gr.setColorAt(1.0, Qt.black)
+ gr.setColorAt(0.8, Qt.darkGreen)
+ gr.setColorAt(0.6, Qt.green)
+ gr.setColorAt(0.4, Qt.yellow)
+ gr.setColorAt(0.2, Qt.red)
+ gr.setColorAt(0.0, Qt.darkRed)
+ pmHighlight = QPixmap(WIDTH, HEIGHT)
+ pmHighlight.fill(Qt.transparent)
+ with QPainter(pmHighlight) as pmpHighlight:
+ pmpHighlight.setBrush(QBrush(gr))
+ pmpHighlight.setPen(Qt.NoPen)
+ pmpHighlight.drawRect(BORDER, BORDER, 35, HEIGHT - 2 * BORDER)
+ pmpHighlight.setPen(Qt.black)
+ step = (HEIGHT - 2 * BORDER) / 5
+ for i in range(0, 6):
+ yPos = i * step + BORDER
+ pmpHighlight.drawLine(BORDER, yPos, 55, yPos)
+ HEIGHT = 550 - (i * 110)
+ pmpHighlight.drawText(60, yPos + 2, f"{HEIGHT} m")
+ return pmHighlight
+
+
+class SurfaceGraph(QObject):
+
+ def __init__(self, minimum_graph_size, maximum_graph_size):
+ super().__init__()
+ self._surfaceGraph = Q3DSurface()
+ self._surfaceWidget = QWidget()
+ hLayout = QHBoxLayout(self._surfaceWidget)
+ self._surfaceGraph.setMinimumSize(minimum_graph_size)
+ self._surfaceGraph.setMaximumSize(maximum_graph_size)
+ self._surfaceGraph.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ self._surfaceGraph.setFocusPolicy(Qt.StrongFocus)
+ self._surfaceGraph.setResizeMode(QQuickWidget.SizeRootObjectToView)
+ hLayout.addWidget(self._surfaceGraph, 1)
+ vLayout = QVBoxLayout()
+ hLayout.addLayout(vLayout)
+ vLayout.setAlignment(Qt.AlignTop)
+ # Create control widgets
+ modelGroupBox = QGroupBox("Model")
+ sqrtSinModelRB = QRadioButton(self._surfaceWidget)
+ sqrtSinModelRB.setText("Sqrt and Sin")
+ sqrtSinModelRB.setChecked(False)
+ heightMapModelRB = QRadioButton(self._surfaceWidget)
+ heightMapModelRB.setText("Multiseries\nHeight Map")
+ heightMapModelRB.setChecked(False)
+ texturedModelRB = QRadioButton(self._surfaceWidget)
+ texturedModelRB.setText("Textured\nTopography")
+ texturedModelRB.setChecked(False)
+ modelVBox = QVBoxLayout()
+ modelVBox.addWidget(sqrtSinModelRB)
+ modelVBox.addWidget(heightMapModelRB)
+ modelVBox.addWidget(texturedModelRB)
+ modelGroupBox.setLayout(modelVBox)
+ selectionGroupBox = QGroupBox("Graph Selection Mode")
+ modeNoneRB = QRadioButton(self._surfaceWidget)
+ modeNoneRB.setText("No selection")
+ modeNoneRB.setChecked(False)
+ modeItemRB = QRadioButton(self._surfaceWidget)
+ modeItemRB.setText("Item")
+ modeItemRB.setChecked(False)
+ modeSliceRowRB = QRadioButton(self._surfaceWidget)
+ modeSliceRowRB.setText("Row Slice")
+ modeSliceRowRB.setChecked(False)
+ modeSliceColumnRB = QRadioButton(self._surfaceWidget)
+ modeSliceColumnRB.setText("Column Slice")
+ modeSliceColumnRB.setChecked(False)
+ selectionVBox = QVBoxLayout()
+ selectionVBox.addWidget(modeNoneRB)
+ selectionVBox.addWidget(modeItemRB)
+ selectionVBox.addWidget(modeSliceRowRB)
+ selectionVBox.addWidget(modeSliceColumnRB)
+ selectionGroupBox.setLayout(selectionVBox)
+ axisGroupBox = QGroupBox("Axis ranges")
+ axisMinSliderX = QSlider(Qt.Horizontal)
+ axisMinSliderX.setMinimum(0)
+ axisMinSliderX.setTickInterval(1)
+ axisMinSliderX.setEnabled(True)
+ axisMaxSliderX = QSlider(Qt.Horizontal)
+ axisMaxSliderX.setMinimum(1)
+ axisMaxSliderX.setTickInterval(1)
+ axisMaxSliderX.setEnabled(True)
+ axisMinSliderZ = QSlider(Qt.Horizontal)
+ axisMinSliderZ.setMinimum(0)
+ axisMinSliderZ.setTickInterval(1)
+ axisMinSliderZ.setEnabled(True)
+ axisMaxSliderZ = QSlider(Qt.Horizontal)
+ axisMaxSliderZ.setMinimum(1)
+ axisMaxSliderZ.setTickInterval(1)
+ axisMaxSliderZ.setEnabled(True)
+ axisVBox = QVBoxLayout(axisGroupBox)
+ axisVBox.addWidget(QLabel("Column range"))
+ axisVBox.addWidget(axisMinSliderX)
+ axisVBox.addWidget(axisMaxSliderX)
+ axisVBox.addWidget(QLabel("Row range"))
+ axisVBox.addWidget(axisMinSliderZ)
+ axisVBox.addWidget(axisMaxSliderZ)
+ # Mode-dependent controls
+ # sqrt-sin
+ colorGroupBox = QGroupBox("Custom gradient")
+
+ pixmap = gradientBtoYPB_Pixmap()
+ gradientBtoYPB = QPushButton(self._surfaceWidget)
+ gradientBtoYPB.setIcon(QIcon(pixmap))
+ gradientBtoYPB.setIconSize(pixmap.size())
+
+ pixmap = gradientGtoRPB_Pixmap()
+ gradientGtoRPB = QPushButton(self._surfaceWidget)
+ gradientGtoRPB.setIcon(QIcon(pixmap))
+ gradientGtoRPB.setIconSize(pixmap.size())
+
+ colorHBox = QHBoxLayout(colorGroupBox)
+ colorHBox.addWidget(gradientBtoYPB)
+ colorHBox.addWidget(gradientGtoRPB)
+ # Multiseries heightmap
+ showGroupBox = QGroupBox("Show Object")
+ showGroupBox.setVisible(False)
+ checkboxShowOilRigOne = QCheckBox("Oil Rig 1")
+ checkboxShowOilRigOne.setChecked(True)
+ checkboxShowOilRigTwo = QCheckBox("Oil Rig 2")
+ checkboxShowOilRigTwo.setChecked(True)
+ checkboxShowRefinery = QCheckBox("Refinery")
+ showVBox = QVBoxLayout()
+ showVBox.addWidget(checkboxShowOilRigOne)
+ showVBox.addWidget(checkboxShowOilRigTwo)
+ showVBox.addWidget(checkboxShowRefinery)
+ showGroupBox.setLayout(showVBox)
+ visualsGroupBox = QGroupBox("Visuals")
+ visualsGroupBox.setVisible(False)
+ checkboxVisualsSeeThrough = QCheckBox("See-Through")
+ checkboxHighlightOil = QCheckBox("Highlight Oil")
+ checkboxShowShadows = QCheckBox("Shadows")
+ checkboxShowShadows.setChecked(True)
+ visualVBox = QVBoxLayout(visualsGroupBox)
+ visualVBox.addWidget(checkboxVisualsSeeThrough)
+ visualVBox.addWidget(checkboxHighlightOil)
+ visualVBox.addWidget(checkboxShowShadows)
+ labelSelection = QLabel("Selection:")
+ labelSelection.setVisible(False)
+ labelSelectedItem = QLabel("Nothing")
+ labelSelectedItem.setVisible(False)
+ # Textured topography heightmap
+ enableTexture = QCheckBox("Surface texture")
+ enableTexture.setVisible(False)
+
+ label = QLabel(self._surfaceWidget)
+ label.setPixmap(highlightPixmap())
+ heightMapGroupBox = QGroupBox("Highlight color map")
+ colorMapVBox = QVBoxLayout()
+ colorMapVBox.addWidget(label)
+ heightMapGroupBox.setLayout(colorMapVBox)
+ heightMapGroupBox.setVisible(False)
+ # Populate vertical layout
+ # Common
+ vLayout.addWidget(modelGroupBox)
+ vLayout.addWidget(selectionGroupBox)
+ vLayout.addWidget(axisGroupBox)
+ # Sqrt Sin
+ vLayout.addWidget(colorGroupBox)
+ # Multiseries heightmap
+ vLayout.addWidget(showGroupBox)
+ vLayout.addWidget(visualsGroupBox)
+ vLayout.addWidget(labelSelection)
+ vLayout.addWidget(labelSelectedItem)
+ # Textured topography
+ vLayout.addWidget(heightMapGroupBox)
+ vLayout.addWidget(enableTexture)
+ # Create the controller
+ modifier = SurfaceGraphModifier(self._surfaceGraph, labelSelectedItem, self)
+ # Connect widget controls to controller
+ heightMapModelRB.toggled.connect(modifier.enableHeightMapModel)
+ sqrtSinModelRB.toggled.connect(modifier.enableSqrtSinModel)
+ texturedModelRB.toggled.connect(modifier.enableTopographyModel)
+ modeNoneRB.toggled.connect(modifier.toggleModeNone)
+ modeItemRB.toggled.connect(modifier.toggleModeItem)
+ modeSliceRowRB.toggled.connect(modifier.toggleModeSliceRow)
+ modeSliceColumnRB.toggled.connect(modifier.toggleModeSliceColumn)
+ axisMinSliderX.valueChanged.connect(modifier.adjustXMin)
+ axisMaxSliderX.valueChanged.connect(modifier.adjustXMax)
+ axisMinSliderZ.valueChanged.connect(modifier.adjustZMin)
+ axisMaxSliderZ.valueChanged.connect(modifier.adjustZMax)
+ # Mode dependent connections
+ gradientBtoYPB.pressed.connect(modifier.setBlackToYellowGradient)
+ gradientGtoRPB.pressed.connect(modifier.setGreenToRedGradient)
+ checkboxShowOilRigOne.stateChanged.connect(modifier.toggleItemOne)
+ checkboxShowOilRigTwo.stateChanged.connect(modifier.toggleItemTwo)
+ checkboxShowRefinery.stateChanged.connect(modifier.toggleItemThree)
+ checkboxVisualsSeeThrough.stateChanged.connect(modifier.toggleSeeThrough)
+ checkboxHighlightOil.stateChanged.connect(modifier.toggleOilHighlight)
+ checkboxShowShadows.stateChanged.connect(modifier.toggleShadows)
+ enableTexture.stateChanged.connect(modifier.toggleSurfaceTexture)
+ # Connections to disable features depending on mode
+ sqrtSinModelRB.toggled.connect(colorGroupBox.setVisible)
+ heightMapModelRB.toggled.connect(showGroupBox.setVisible)
+ heightMapModelRB.toggled.connect(visualsGroupBox.setVisible)
+ heightMapModelRB.toggled.connect(labelSelection.setVisible)
+ heightMapModelRB.toggled.connect(labelSelectedItem.setVisible)
+ texturedModelRB.toggled.connect(enableTexture.setVisible)
+ texturedModelRB.toggled.connect(heightMapGroupBox.setVisible)
+ modifier.setAxisMinSliderX(axisMinSliderX)
+ modifier.setAxisMaxSliderX(axisMaxSliderX)
+ modifier.setAxisMinSliderZ(axisMinSliderZ)
+ modifier.setAxisMaxSliderZ(axisMaxSliderZ)
+ sqrtSinModelRB.setChecked(True)
+ modeItemRB.setChecked(True)
+ enableTexture.setChecked(True)
+
+ def surfaceWidget(self):
+ return self._surfaceWidget
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import os
+from math import sqrt, sin
+from pathlib import Path
+
+from PySide6.QtCore import QObject, QPropertyAnimation, Qt, Slot
+from PySide6.QtGui import (QColor, QFont, QImage, QLinearGradient,
+ QQuaternion, QVector3D)
+from PySide6.QtGraphs import (QAbstract3DGraph, QCustom3DItem,
+ QCustom3DLabel, QHeightMapSurfaceDataProxy,
+ QValue3DAxis, QSurfaceDataItem,
+ QSurfaceDataProxy, QSurface3DSeries,
+ Q3DInputHandler, Q3DTheme)
+
+
+from highlightseries import HighlightSeries
+from topographicseries import TopographicSeries
+from custominputhandler import CustomInputHandler
+
+
+SAMPLE_COUNT_X = 150
+SAMPLE_COUNT_Z = 150
+HEIGHTMAP_GRID_STEP_X = 6
+HEIGHTMAP_GRID_STEP_Z = 6
+SAMPLE_MIN = -8.0
+SAMPLE_MAX = 8.0
+
+AREA_WIDTH = 8000.0
+AREA_HEIGHT = 8000.0
+ASPECT_RATIO = 0.1389
+MIN_RANGE = AREA_WIDTH * 0.49
+
+
+class SurfaceGraphModifier(QObject):
+
+ def __init__(self, surface, label, parent):
+ super().__init__(parent)
+ self._data_path = Path(__file__).resolve().parent / "data"
+ self._graph = surface
+ self._textField = label
+ self._sqrtSinProxy = None
+ self._sqrtSinSeries = None
+ self._heightMapProxyOne = None
+ self._heightMapProxyTwo = None
+ self._heightMapProxyThree = None
+ self._heightMapSeriesOne = None
+ self._heightMapSeriesTwo = None
+ self._heightMapSeriesThree = None
+
+ self._axisMinSliderX = None
+ self._axisMaxSliderX = None
+ self._axisMinSliderZ = None
+ self._axisMaxSliderZ = None
+ self._rangeMinX = 0.0
+ self._rangeMinZ = 0.0
+ self._stepX = 0.0
+ self._stepZ = 0.0
+ self._heightMapWidth = 0
+ self._heightMapHeight = 0
+
+ self._selectionAnimation = None
+ self._titleLabel = None
+ self._previouslyAnimatedItem = None
+ self._previousScaling = {}
+
+ self._topography = None
+ self._highlight = None
+ self._highlightWidth = 0
+ self._highlightHeight = 0
+
+ self._customInputHandler = None
+ self._defaultInputHandler = Q3DInputHandler()
+
+ self._graph.setCameraZoomLevel(85.0)
+ self._graph.setCameraPreset(QAbstract3DGraph.CameraPreset.IsometricRight)
+ self._graph.activeTheme().setType(Q3DTheme.Theme.Retro)
+
+ self._x_axis = QValue3DAxis()
+ self._y_axis = QValue3DAxis()
+ self._z_axis = QValue3DAxis()
+ self._graph.setAxisX(self._x_axis)
+ self._graph.setAxisY(self._y_axis)
+ self._graph.setAxisZ(self._z_axis)
+
+ #
+ # Sqrt Sin
+ #
+ self._sqrtSinProxy = QSurfaceDataProxy()
+ self._sqrtSinSeries = QSurface3DSeries(self._sqrtSinProxy)
+ self.fillSqrtSinProxy()
+
+ #
+ # Multisurface heightmap
+ #
+ # Create the first surface layer
+ heightMapImageOne = QImage(self._data_path / "layer_1.png")
+ self._heightMapProxyOne = QHeightMapSurfaceDataProxy(heightMapImageOne)
+ self._heightMapSeriesOne = QSurface3DSeries(self._heightMapProxyOne)
+ self._heightMapSeriesOne.setItemLabelFormat("(@xLabel, @zLabel): @yLabel")
+ self._heightMapProxyOne.setValueRanges(34.0, 40.0, 18.0, 24.0)
+
+ # Create the other 2 surface layers
+ heightMapImageTwo = QImage(self._data_path / "layer_2.png")
+ self._heightMapProxyTwo = QHeightMapSurfaceDataProxy(heightMapImageTwo)
+ self._heightMapSeriesTwo = QSurface3DSeries(self._heightMapProxyTwo)
+ self._heightMapSeriesTwo.setItemLabelFormat("(@xLabel, @zLabel): @yLabel")
+ self._heightMapProxyTwo.setValueRanges(34.0, 40.0, 18.0, 24.0)
+
+ heightMapImageThree = QImage(self._data_path / "layer_3.png")
+ self._heightMapProxyThree = QHeightMapSurfaceDataProxy(heightMapImageThree)
+ self._heightMapSeriesThree = QSurface3DSeries(self._heightMapProxyThree)
+ self._heightMapSeriesThree.setItemLabelFormat("(@xLabel, @zLabel): @yLabel")
+ self._heightMapProxyThree.setValueRanges(34.0, 40.0, 18.0, 24.0)
+
+ # The images are the same size, so it's enough to get the dimensions
+ # from one
+ self._heightMapWidth = heightMapImageOne.width()
+ self._heightMapHeight = heightMapImageOne.height()
+
+ # Set the gradients for multi-surface layers
+ grOne = QLinearGradient()
+ grOne.setColorAt(0.0, Qt.black)
+ grOne.setColorAt(0.38, Qt.darkYellow)
+ grOne.setColorAt(0.39, Qt.darkGreen)
+ grOne.setColorAt(0.5, Qt.darkGray)
+ grOne.setColorAt(1.0, Qt.gray)
+ self._heightMapSeriesOne.setBaseGradient(grOne)
+ self._heightMapSeriesOne.setColorStyle(Q3DTheme.ColorStyle.RangeGradient)
+
+ grTwo = QLinearGradient()
+ grTwo.setColorAt(0.39, Qt.blue)
+ grTwo.setColorAt(0.4, Qt.white)
+ self._heightMapSeriesTwo.setBaseGradient(grTwo)
+ self._heightMapSeriesTwo.setColorStyle(Q3DTheme.ColorStyle.RangeGradient)
+
+ grThree = QLinearGradient()
+ grThree.setColorAt(0.0, Qt.white)
+ grThree.setColorAt(0.05, Qt.black)
+ self._heightMapSeriesThree.setBaseGradient(grThree)
+ self._heightMapSeriesThree.setColorStyle(Q3DTheme.ColorStyle.RangeGradient)
+
+ # Custom items and label
+ self._graph.selectedElementChanged.connect(self.handleElementSelected)
+
+ self._selectionAnimation = QPropertyAnimation(self)
+ self._selectionAnimation.setPropertyName(b"scaling")
+ self._selectionAnimation.setDuration(500)
+ self._selectionAnimation.setLoopCount(-1)
+
+ titleFont = QFont("Century Gothic", 30)
+ titleFont.setBold(True)
+ self._titleLabel = QCustom3DLabel("Oil Rigs on Imaginary Sea", titleFont,
+ QVector3D(0.0, 1.2, 0.0),
+ QVector3D(1.0, 1.0, 0.0),
+ QQuaternion())
+ self._titleLabel.setPositionAbsolute(True)
+ self._titleLabel.setFacingCamera(True)
+ self._titleLabel.setBackgroundColor(QColor(0x66cdaa))
+ self._graph.addCustomItem(self._titleLabel)
+ self._titleLabel.setVisible(False)
+
+ # Make two of the custom object visible
+ self.toggleItemOne(True)
+ self.toggleItemTwo(True)
+
+ #
+ # Topographic map
+ #
+ self._topography = TopographicSeries()
+ file_name = os.fspath(self._data_path / "topography.png")
+ self._topography.setTopographyFile(file_name, AREA_WIDTH, AREA_HEIGHT)
+ self._topography.setItemLabelFormat("@yLabel m")
+
+ self._highlight = HighlightSeries()
+ self._highlight.setTopographicSeries(self._topography)
+ self._highlight.setMinHeight(MIN_RANGE * ASPECT_RATIO)
+ self._highlight.handleGradientChange(AREA_WIDTH * ASPECT_RATIO)
+ self._graph.axisY().maxChanged.connect(self._highlight.handleGradientChange)
+
+ self._customInputHandler = CustomInputHandler(self._graph)
+ self._customInputHandler.setHighlightSeries(self._highlight)
+ self._customInputHandler.setAxes(self._x_axis, self._y_axis, self._z_axis)
+ self._customInputHandler.setLimits(0.0, AREA_WIDTH, MIN_RANGE)
+ self._customInputHandler.setAspectRatio(ASPECT_RATIO)
+
+ def fillSqrtSinProxy(self):
+ stepX = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_X - 1)
+ stepZ = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_Z - 1)
+
+ dataArray = []
+ for i in range(0, SAMPLE_COUNT_Z):
+ newRow = []
+ # Keep values within range bounds, since just adding step can
+ # cause minor drift due to the rounding errors.
+ z = min(SAMPLE_MAX, (i * stepZ + SAMPLE_MIN))
+ for j in range(0, SAMPLE_COUNT_X):
+ x = min(SAMPLE_MAX, (j * stepX + SAMPLE_MIN))
+ R = sqrt(z * z + x * x) + 0.01
+ y = (sin(R) / R + 0.24) * 1.61
+ item = QSurfaceDataItem(QVector3D(x, y, z))
+ newRow.append(item)
+ dataArray.append(newRow)
+ self._sqrtSinProxy.resetArray(dataArray)
+
+ @Slot(bool)
+ def enableSqrtSinModel(self, enable):
+ if enable:
+ self._sqrtSinSeries.setDrawMode(QSurface3DSeries.DrawSurfaceAndWireframe)
+ self._sqrtSinSeries.setFlatShadingEnabled(True)
+
+ self._graph.axisX().setLabelFormat("%.2f")
+ self._graph.axisZ().setLabelFormat("%.2f")
+ self._graph.axisX().setRange(SAMPLE_MIN, SAMPLE_MAX)
+ self._graph.axisY().setRange(0.0, 2.0)
+ self._graph.axisZ().setRange(SAMPLE_MIN, SAMPLE_MAX)
+ self._graph.axisX().setLabelAutoRotation(30.0)
+ self._graph.axisY().setLabelAutoRotation(90.0)
+ self._graph.axisZ().setLabelAutoRotation(30.0)
+
+ self._graph.removeSeries(self._heightMapSeriesOne)
+ self._graph.removeSeries(self._heightMapSeriesTwo)
+ self._graph.removeSeries(self._heightMapSeriesThree)
+ self._graph.removeSeries(self._topography)
+ self._graph.removeSeries(self._highlight)
+
+ self._graph.addSeries(self._sqrtSinSeries)
+
+ self._titleLabel.setVisible(False)
+ self._graph.axisX().setTitleVisible(False)
+ self._graph.axisY().setTitleVisible(False)
+ self._graph.axisZ().setTitleVisible(False)
+
+ self._graph.axisX().setTitle("")
+ self._graph.axisY().setTitle("")
+ self._graph.axisZ().setTitle("")
+
+ self._graph.setActiveInputHandler(self._defaultInputHandler)
+
+ # Reset range sliders for Sqrt & Sin
+ self._rangeMinX = SAMPLE_MIN
+ self._rangeMinZ = SAMPLE_MIN
+ self._stepX = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_X - 1)
+ self._stepZ = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_Z - 1)
+ self._axisMinSliderX.setMinimum(0)
+ self._axisMinSliderX.setMaximum(SAMPLE_COUNT_X - 2)
+ self._axisMinSliderX.setValue(0)
+ self._axisMaxSliderX.setMinimum(1)
+ self._axisMaxSliderX.setMaximum(SAMPLE_COUNT_X - 1)
+ self._axisMaxSliderX.setValue(SAMPLE_COUNT_X - 1)
+ self._axisMinSliderZ.setMinimum(0)
+ self._axisMinSliderZ.setMaximum(SAMPLE_COUNT_Z - 2)
+ self._axisMinSliderZ.setValue(0)
+ self._axisMaxSliderZ.setMinimum(1)
+ self._axisMaxSliderZ.setMaximum(SAMPLE_COUNT_Z - 1)
+ self._axisMaxSliderZ.setValue(SAMPLE_COUNT_Z - 1)
+
+ @Slot(bool)
+ def enableHeightMapModel(self, enable):
+ if enable:
+ self._heightMapSeriesOne.setDrawMode(QSurface3DSeries.DrawSurface)
+ self._heightMapSeriesOne.setFlatShadingEnabled(False)
+ self._heightMapSeriesTwo.setDrawMode(QSurface3DSeries.DrawSurface)
+ self._heightMapSeriesTwo.setFlatShadingEnabled(False)
+ self._heightMapSeriesThree.setDrawMode(QSurface3DSeries.DrawSurface)
+ self._heightMapSeriesThree.setFlatShadingEnabled(False)
+
+ self._graph.axisX().setLabelFormat("%.1f N")
+ self._graph.axisZ().setLabelFormat("%.1f E")
+ self._graph.axisX().setRange(34.0, 40.0)
+ self._graph.axisY().setAutoAdjustRange(True)
+ self._graph.axisZ().setRange(18.0, 24.0)
+
+ self._graph.axisX().setTitle("Latitude")
+ self._graph.axisY().setTitle("Height")
+ self._graph.axisZ().setTitle("Longitude")
+
+ self._graph.removeSeries(self._sqrtSinSeries)
+ self._graph.removeSeries(self._topography)
+ self._graph.removeSeries(self._highlight)
+ self._graph.addSeries(self._heightMapSeriesOne)
+ self._graph.addSeries(self._heightMapSeriesTwo)
+ self._graph.addSeries(self._heightMapSeriesThree)
+
+ self._graph.setActiveInputHandler(self._defaultInputHandler)
+
+ self._titleLabel.setVisible(True)
+ self._graph.axisX().setTitleVisible(True)
+ self._graph.axisY().setTitleVisible(True)
+ self._graph.axisZ().setTitleVisible(True)
+
+ # Reset range sliders for height map
+ mapGridCountX = self._heightMapWidth / HEIGHTMAP_GRID_STEP_X
+ mapGridCountZ = self._heightMapHeight / HEIGHTMAP_GRID_STEP_Z
+ self._rangeMinX = 34.0
+ self._rangeMinZ = 18.0
+ self._stepX = 6.0 / float(mapGridCountX - 1)
+ self._stepZ = 6.0 / float(mapGridCountZ - 1)
+ self._axisMinSliderX.setMinimum(0)
+ self._axisMinSliderX.setMaximum(mapGridCountX - 2)
+ self._axisMinSliderX.setValue(0)
+ self._axisMaxSliderX.setMinimum(1)
+ self._axisMaxSliderX.setMaximum(mapGridCountX - 1)
+ self._axisMaxSliderX.setValue(mapGridCountX - 1)
+ self._axisMinSliderZ.setMinimum(0)
+ self._axisMinSliderZ.setMaximum(mapGridCountZ - 2)
+ self._axisMinSliderZ.setValue(0)
+ self._axisMaxSliderZ.setMinimum(1)
+ self._axisMaxSliderZ.setMaximum(mapGridCountZ - 1)
+ self._axisMaxSliderZ.setValue(mapGridCountZ - 1)
+
+ @Slot(bool)
+ def enableTopographyModel(self, enable):
+ if enable:
+ self._graph.axisX().setLabelFormat("%i")
+ self._graph.axisZ().setLabelFormat("%i")
+ self._graph.axisX().setRange(0.0, AREA_WIDTH)
+ self._graph.axisY().setRange(100.0, AREA_WIDTH * ASPECT_RATIO)
+ self._graph.axisZ().setRange(0.0, AREA_HEIGHT)
+ self._graph.axisX().setLabelAutoRotation(30.0)
+ self._graph.axisY().setLabelAutoRotation(90.0)
+ self._graph.axisZ().setLabelAutoRotation(30.0)
+
+ self._graph.removeSeries(self._heightMapSeriesOne)
+ self._graph.removeSeries(self._heightMapSeriesTwo)
+ self._graph.removeSeries(self._heightMapSeriesThree)
+ self._graph.addSeries(self._topography)
+ self._graph.addSeries(self._highlight)
+
+ self._titleLabel.setVisible(False)
+ self._graph.axisX().setTitleVisible(False)
+ self._graph.axisY().setTitleVisible(False)
+ self._graph.axisZ().setTitleVisible(False)
+
+ self._graph.axisX().setTitle("")
+ self._graph.axisY().setTitle("")
+ self._graph.axisZ().setTitle("")
+
+ self._graph.setActiveInputHandler(self._customInputHandler)
+
+ # Reset range sliders for topography map
+ self._rangeMinX = 0.0
+ self._rangeMinZ = 0.0
+ self._stepX = 1.0
+ self._stepZ = 1.0
+ self._axisMinSliderX.setMinimum(0)
+ self._axisMinSliderX.setMaximum(AREA_WIDTH - 200)
+ self._axisMinSliderX.setValue(0)
+ self._axisMaxSliderX.setMinimum(200)
+ self._axisMaxSliderX.setMaximum(AREA_WIDTH)
+ self._axisMaxSliderX.setValue(AREA_WIDTH)
+ self._axisMinSliderZ.setMinimum(0)
+ self._axisMinSliderZ.setMaximum(AREA_HEIGHT - 200)
+ self._axisMinSliderZ.setValue(0)
+ self._axisMaxSliderZ.setMinimum(200)
+ self._axisMaxSliderZ.setMaximum(AREA_HEIGHT)
+ self._axisMaxSliderZ.setValue(AREA_HEIGHT)
+
+ def adjustXMin(self, min):
+ minX = self._stepX * float(min) + self._rangeMinX
+
+ max = self._axisMaxSliderX.value()
+ if min >= max:
+ max = min + 1
+ self._axisMaxSliderX.setValue(max)
+
+ maxX = self._stepX * max + self._rangeMinX
+
+ self.setAxisXRange(minX, maxX)
+
+ def adjustXMax(self, max):
+ maxX = self._stepX * float(max) + self._rangeMinX
+
+ min = self._axisMinSliderX.value()
+ if max <= min:
+ min = max - 1
+ self._axisMinSliderX.setValue(min)
+
+ minX = self._stepX * min + self._rangeMinX
+
+ self.setAxisXRange(minX, maxX)
+
+ def adjustZMin(self, min):
+ minZ = self._stepZ * float(min) + self._rangeMinZ
+
+ max = self._axisMaxSliderZ.value()
+ if min >= max:
+ max = min + 1
+ self._axisMaxSliderZ.setValue(max)
+
+ maxZ = self._stepZ * max + self._rangeMinZ
+
+ self.setAxisZRange(minZ, maxZ)
+
+ def adjustZMax(self, max):
+ maxX = self._stepZ * float(max) + self._rangeMinZ
+
+ min = self._axisMinSliderZ.value()
+ if max <= min:
+ min = max - 1
+ self._axisMinSliderZ.setValue(min)
+
+ minX = self._stepZ * min + self._rangeMinZ
+
+ self.setAxisZRange(minX, maxX)
+
+ def setAxisXRange(self, min, max):
+ self._graph.axisX().setRange(min, max)
+
+ def setAxisZRange(self, min, max):
+ self._graph.axisZ().setRange(min, max)
+
+ def setBlackToYellowGradient(self):
+ gr = QLinearGradient()
+ gr.setColorAt(0.0, Qt.black)
+ gr.setColorAt(0.33, Qt.blue)
+ gr.setColorAt(0.67, Qt.red)
+ gr.setColorAt(1.0, Qt.yellow)
+
+ self._sqrtSinSeries.setBaseGradient(gr)
+ self._sqrtSinSeries.setColorStyle(Q3DTheme.ColorStyle.RangeGradient)
+
+ def setGreenToRedGradient(self):
+ gr = QLinearGradient()
+ gr.setColorAt(0.0, Qt.darkGreen)
+ gr.setColorAt(0.5, Qt.yellow)
+ gr.setColorAt(0.8, Qt.red)
+ gr.setColorAt(1.0, Qt.darkRed)
+
+ self._sqrtSinSeries.setBaseGradient(gr)
+ self._sqrtSinSeries.setColorStyle(Q3DTheme.ColorStyle.RangeGradient)
+
+ @Slot(bool)
+ def toggleItemOne(self, show):
+ positionOne = QVector3D(39.0, 77.0, 19.2)
+ positionOnePipe = QVector3D(39.0, 45.0, 19.2)
+ positionOneLabel = QVector3D(39.0, 107.0, 19.2)
+ if show:
+ color = QImage(2, 2, QImage.Format_RGB32)
+ color.fill(Qt.red)
+ file_name = os.fspath(self._data_path / "oilrig.mesh")
+ item = QCustom3DItem(file_name, positionOne,
+ QVector3D(0.025, 0.025, 0.025),
+ QQuaternion.fromAxisAndAngle(0.0, 1.0, 0.0, 45.0),
+ color)
+ self._graph.addCustomItem(item)
+ file_name = os.fspath(self._data_path / "pipe.mesh")
+ item = QCustom3DItem(file_name, positionOnePipe,
+ QVector3D(0.005, 0.5, 0.005), QQuaternion(),
+ color)
+ item.setShadowCasting(False)
+ self._graph.addCustomItem(item)
+
+ label = QCustom3DLabel()
+ label.setText("Oil Rig One")
+ label.setPosition(positionOneLabel)
+ label.setScaling(QVector3D(1.0, 1.0, 1.0))
+ self._graph.addCustomItem(label)
+ else:
+ self.resetSelection()
+ self._graph.removeCustomItemAt(positionOne)
+ self._graph.removeCustomItemAt(positionOnePipe)
+ self._graph.removeCustomItemAt(positionOneLabel)
+
+ @Slot(bool)
+ def toggleItemTwo(self, show):
+ positionTwo = QVector3D(34.5, 77.0, 23.4)
+ positionTwoPipe = QVector3D(34.5, 45.0, 23.4)
+ positionTwoLabel = QVector3D(34.5, 107.0, 23.4)
+ if show:
+ color = QImage(2, 2, QImage.Format_RGB32)
+ color.fill(Qt.red)
+ item = QCustom3DItem()
+ file_name = os.fspath(self._data_path / "oilrig.mesh")
+ item.setMeshFile(file_name)
+ item.setPosition(positionTwo)
+ item.setScaling(QVector3D(0.025, 0.025, 0.025))
+ item.setRotation(QQuaternion.fromAxisAndAngle(0.0, 1.0, 0.0, 25.0))
+ item.setTextureImage(color)
+ self._graph.addCustomItem(item)
+ file_name = os.fspath(self._data_path / "pipe.mesh")
+ item = QCustom3DItem(file_name, positionTwoPipe,
+ QVector3D(0.005, 0.5, 0.005), QQuaternion(),
+ color)
+ item.setShadowCasting(False)
+ self._graph.addCustomItem(item)
+
+ label = QCustom3DLabel()
+ label.setText("Oil Rig Two")
+ label.setPosition(positionTwoLabel)
+ label.setScaling(QVector3D(1.0, 1.0, 1.0))
+ self._graph.addCustomItem(label)
+ else:
+ self.resetSelection()
+ self._graph.removeCustomItemAt(positionTwo)
+ self._graph.removeCustomItemAt(positionTwoPipe)
+ self._graph.removeCustomItemAt(positionTwoLabel)
+
+ @Slot(bool)
+ def toggleItemThree(self, show):
+ positionThree = QVector3D(34.5, 86.0, 19.1)
+ positionThreeLabel = QVector3D(34.5, 116.0, 19.1)
+ if show:
+ color = QImage(2, 2, QImage.Format_RGB32)
+ color.fill(Qt.darkMagenta)
+ item = QCustom3DItem()
+ file_name = os.fspath(self._data_path / "refinery.mesh")
+ item.setMeshFile(file_name)
+ item.setPosition(positionThree)
+ item.setScaling(QVector3D(0.04, 0.04, 0.04))
+ item.setRotation(QQuaternion.fromAxisAndAngle(0.0, 1.0, 0.0, 75.0))
+ item.setTextureImage(color)
+ self._graph.addCustomItem(item)
+
+ label = QCustom3DLabel()
+ label.setText("Refinery")
+ label.setPosition(positionThreeLabel)
+ label.setScaling(QVector3D(1.0, 1.0, 1.0))
+ self._graph.addCustomItem(label)
+ else:
+ self.resetSelection()
+ self._graph.removeCustomItemAt(positionThree)
+ self._graph.removeCustomItemAt(positionThreeLabel)
+
+ @Slot(bool)
+ def toggleSeeThrough(self, seethrough):
+ s0 = self._graph.seriesList()[0]
+ s1 = self._graph.seriesList()[1]
+ if seethrough:
+ s0.setDrawMode(QSurface3DSeries.DrawWireframe)
+ s1.setDrawMode(QSurface3DSeries.DrawWireframe)
+ else:
+ s0.setDrawMode(QSurface3DSeries.DrawSurface)
+ s1.setDrawMode(QSurface3DSeries.DrawSurface)
+
+ @Slot(bool)
+ def toggleOilHighlight(self, highlight):
+ s2 = self._graph.seriesList()[2]
+ if highlight:
+ grThree = QLinearGradient()
+ grThree.setColorAt(0.0, Qt.black)
+ grThree.setColorAt(0.05, Qt.red)
+ s2.setBaseGradient(grThree)
+ else:
+ grThree = QLinearGradient()
+ grThree.setColorAt(0.0, Qt.white)
+ grThree.setColorAt(0.05, Qt.black)
+ s2.setBaseGradient(grThree)
+
+ @Slot(bool)
+ def toggleShadows(self, shadows):
+ sq = (QAbstract3DGraph.ShadowQualityMedium
+ if shadows else QAbstract3DGraph.ShadowQualityNone)
+ self._graph.setShadowQuality(sq)
+
+ @Slot(bool)
+ def toggleSurfaceTexture(self, enable):
+ if enable:
+ file_name = os.fspath(self._data_path / "maptexture.jpg")
+ self._topography.setTextureFile(file_name)
+ else:
+ self._topography.setTextureFile("")
+
+ def handleElementSelected(self, type):
+ self.resetSelection()
+ if type == QAbstract3DGraph.ElementCustomItem:
+ item = self._graph.selectedCustomItem()
+ text = ""
+ if isinstance(item, QCustom3DItem):
+ text += "Custom label: "
+ else:
+ file = item.meshFile().split("/")[-1]
+ text += f"{file}: "
+
+ text += str(self._graph.selectedCustomItemIndex())
+ self._textField.setText(text)
+ self._previouslyAnimatedItem = item
+ self._previousScaling = item.scaling()
+ self._selectionAnimation.setTargetObject(item)
+ self._selectionAnimation.setStartValue(item.scaling())
+ self._selectionAnimation.setEndValue(item.scaling() * 1.5)
+ self._selectionAnimation.start()
+ elif type == QAbstract3DGraph.ElementSeries:
+ text = "Surface ("
+ series = self._graph.selectedSeries()
+ if series:
+ point = series.selectedPoint()
+ text += f"{point.x()}, {point.y()}"
+ text += ")"
+ self._textField.setText(text)
+ elif (type.value > QAbstract3DGraph.ElementSeries.value
+ and type < QAbstract3DGraph.ElementCustomItem.value):
+ index = self._graph.selectedLabelIndex()
+ text = ""
+ if type == QAbstract3DGraph.ElementAxisXLabel:
+ text += "Axis X label: "
+ elif type == QAbstract3DGraph.ElementAxisYLabel:
+ text += "Axis Y label: "
+ else:
+ text += "Axis Z label: "
+ text += str(index)
+ self._textField.setText(text)
+ else:
+ self._textField.setText("Nothing")
+
+ def resetSelection(self):
+ self._selectionAnimation.stop()
+ if self._previouslyAnimatedItem:
+ self._previouslyAnimatedItem.setScaling(self._previousScaling)
+ self._previouslyAnimatedItem = None
+
+ def toggleModeNone(self):
+ self._graph.setSelectionMode(QAbstract3DGraph.SelectionNone)
+
+ def toggleModeItem(self):
+ self._graph.setSelectionMode(QAbstract3DGraph.SelectionItem)
+
+ def toggleModeSliceRow(self):
+ sm = (QAbstract3DGraph.SelectionItemAndRow
+ | QAbstract3DGraph.SelectionSlice
+ | QAbstract3DGraph.SelectionMultiSeries)
+ self._graph.setSelectionMode(sm)
+
+ def toggleModeSliceColumn(self):
+ sm = (QAbstract3DGraph.SelectionItemAndColumn
+ | QAbstract3DGraph.SelectionSlice
+ | QAbstract3DGraph.SelectionMultiSeries)
+ self._graph.setSelectionMode(sm)
+
+ def setAxisMinSliderX(self, slider):
+ self._axisMinSliderX = slider
+
+ def setAxisMaxSliderX(self, slider):
+ self._axisMaxSliderX = slider
+
+ def setAxisMinSliderZ(self, slider):
+ self._axisMinSliderZ = slider
+
+ def setAxisMaxSliderZ(self, slider):
+ self._axisMaxSliderZ = slider
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import Qt
+from PySide6.QtGui import QImage, QVector3D
+from PySide6.QtGraphs import (QSurface3DSeries, QSurfaceDataItem)
+
+
+# Value used to encode height data as RGB value on PNG file
+PACKING_FACTOR = 11983.0
+
+
+class TopographicSeries(QSurface3DSeries):
+
+ def __init__(self):
+ super().__init__()
+ self._sampleCountX = 0.0
+ self._sampleCountZ = 0.0
+ self.setDrawMode(QSurface3DSeries.DrawSurface)
+ self.setFlatShadingEnabled(True)
+ self.setBaseColor(Qt.white)
+
+ def sampleCountX(self):
+ return self._sampleCountX
+
+ def sampleCountZ(self):
+ return self._sampleCountZ
+
+ def setTopographyFile(self, file, width, height):
+ heightMapImage = QImage(file)
+ bits = heightMapImage.bits()
+ imageHeight = heightMapImage.height()
+ imageWidth = heightMapImage.width()
+ widthBits = imageWidth * 4
+ stepX = width / float(imageWidth)
+ stepZ = height / float(imageHeight)
+
+ dataArray = []
+ for i in range(0, imageHeight):
+ p = i * widthBits
+ z = height - float(i) * stepZ
+ newRow = []
+ for j in range(0, imageWidth):
+ aa = bits[p + 0]
+ rr = bits[p + 1]
+ gg = bits[p + 2]
+ color = (gg << 16) + (rr << 8) + aa
+ y = float(color) / PACKING_FACTOR
+ item = QSurfaceDataItem(QVector3D(float(j) * stepX, y, z))
+ newRow.append(item)
+ p += 4
+ dataArray.append(newRow)
+
+ self.dataProxy().resetArray(dataArray)
+
+ self._sampleCountX = float(imageWidth)
+ self._sampleCountZ = float(imageHeight)
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import QObject, Signal
+
+
+class VariantBarDataMapping(QObject):
+
+ rowIndexChanged = Signal()
+ columnIndexChanged = Signal()
+ valueIndexChanged = Signal()
+ rowCategoriesChanged = Signal()
+ columnCategoriesChanged = Signal()
+ mappingChanged = Signal()
+
+ def __init__(self, rowIndex, columnIndex, valueIndex,
+ rowCategories=[], columnCategories=[]):
+ super().__init__(None)
+ self._rowIndex = rowIndex
+ self._columnIndex = columnIndex
+ self._valueIndex = valueIndex
+ self._rowCategories = rowCategories
+ self._columnCategories = columnCategories
+
+ def setRowIndex(self, index):
+ self._rowIndex = index
+ self.mappingChanged.emit()
+
+ def rowIndex(self):
+ return self._rowIndex
+
+ def setColumnIndex(self, index):
+ self._columnIndex = index
+ self.mappingChanged.emit()
+
+ def columnIndex(self):
+ return self._columnIndex
+
+ def setValueIndex(self, index):
+ self._valueIndex = index
+ self.mappingChanged.emit()
+
+ def valueIndex(self):
+ return self._valueIndex
+
+ def setRowCategories(self, categories):
+ self._rowCategories = categories
+ self.mappingChanged.emit()
+
+ def rowCategories(self):
+ return self._rowCategories
+
+ def setColumnCategories(self, categories):
+ self._columnCategories = categories
+ self.mappingChanged.emit()
+
+ def columnCategories(self):
+ return self._columnCategories
+
+ def remap(self, rowIndex, columnIndex, valueIndex,
+ rowCategories=[], columnCategories=[]):
+ self._rowIndex = rowIndex
+ self._columnIndex = columnIndex
+ self._valueIndex = valueIndex
+ self._rowCategories = rowCategories
+ self._columnCategories = columnCategories
+ self.mappingChanged.emit()
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import Slot
+from PySide6.QtGraphs import QBarDataProxy, QBarDataItem
+
+
+class VariantBarDataProxy(QBarDataProxy):
+
+ def __init__(self):
+ super().__init__()
+ self._dataSet = None
+ self._mapping = None
+
+ def setDataSet(self, newSet):
+ if self._dataSet:
+ self._dataSet.itemsAdded.disconnect(self.handleItemsAdded)
+ self._dataSet.dataCleared.disconnect(self.handleDataCleared)
+
+ self._dataSet = newSet
+
+ if self._dataSet:
+ self._dataSet.itemsAdded.connect(self.handleItemsAdded)
+ self._dataSet.dataCleared.connect(self.handleDataCleared)
+ self.resolveDataSet()
+
+ def dataSet(self):
+ return self._dataSet.data()
+
+ # Map key (row, column, value) to value index in data item (VariantItem).
+ # Doesn't gain ownership of mapping, but does connect to it to listen for
+ # mapping changes. Modifying mapping that is set to proxy will trigger
+ # dataset re-resolving.
+ def setMapping(self, mapping):
+ if self._mapping:
+ self._mapping.mappingChanged.disconnect(self.handleMappingChanged)
+
+ self._mapping = mapping
+
+ if self._mapping:
+ self._mapping.mappingChanged.connect(self.handleMappingChanged)
+
+ self.resolveDataSet()
+
+ def mapping(self):
+ return self._mapping.data()
+
+ @Slot(int, int)
+ def handleItemsAdded(self, index, count):
+ # Resolve new items
+ self.resolveDataSet()
+
+ @Slot()
+ def handleDataCleared(self):
+ # Data cleared, reset array
+ self.resetArray(None)
+
+ @Slot()
+ def handleMappingChanged(self):
+ self.resolveDataSet()
+
+ # Resolve entire dataset into QBarDataArray.
+ def resolveDataSet(self):
+ # If we have no data or mapping, or the categories are not defined,
+ # simply clear the array
+ if (not self._dataSet or not self._mapping
+ or not self._mapping.rowCategories()
+ or not self._mapping.columnCategories()):
+ self.resetArray()
+ return
+
+ itemList = self._dataSet.itemList()
+
+ rowIndex = self._mapping.rowIndex()
+ columnIndex = self._mapping.columnIndex()
+ valueIndex = self._mapping.valueIndex()
+ rowList = self._mapping.rowCategories()
+ columnList = self._mapping.columnCategories()
+
+ # Sort values into rows and columns
+ itemValueMap = {}
+ for item in itemList:
+ key = str(item[rowIndex])
+ v = itemValueMap.get(key)
+ if not v:
+ v = {}
+ itemValueMap[key] = v
+ v[str(item[columnIndex])] = float(item[valueIndex])
+
+ # Create a new data array in format the parent class understands
+ newProxyArray = []
+ for rowKey in rowList:
+ newProxyRow = []
+ for i in range(0, len(columnList)):
+ item = QBarDataItem(itemValueMap[rowKey][columnList[i]])
+ newProxyRow.append(item)
+ newProxyArray.append(newProxyRow)
+
+ # Finally, reset the data array in the parent class
+ self.resetArray(newProxyArray)
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtCore import QObject, Signal
+
+
+class VariantDataSet(QObject):
+
+ itemsAdded = Signal(int, int)
+ dataCleared = Signal()
+
+ def __init__(self):
+ super().__init__()
+ self._variantData = []
+
+ def clear(self):
+ for item in self._variantData:
+ item.clear()
+ del item
+
+ self._variantData.clear()
+ self.dataCleared.emit()
+
+ def addItem(self, item):
+ self._variantData.append(item)
+ addIndex = len(self._variantData)
+
+ self.itemsAdded.emit(addIndex, 1)
+ return addIndex
+
+ def addItems(self, itemList):
+ newCount = len(itemList)
+ addIndex = len(self._variantData)
+ self._variantData.extend(itemList)
+ self.itemsAdded.emit(addIndex, newCount)
+ return addIndex
+
+ def itemList(self):
+ return self._variantData
--- /dev/null
+{
+ "files": ["main.py",
+ "axesinputhandler.py",
+ "bargraph.py",
+ "custominputhandler.py",
+ "graphmodifier.py",
+ "highlightseries.py",
+ "rainfalldata.py",
+ "scatterdatamodifier.py",
+ "scattergraph.py",
+ "surfacegraph.py",
+ "surfacegraphmodifier.py",
+ "topographicseries.py",
+ "variantbardatamapping.py",
+ "variantbardataproxy.py",
+ "variantdataset.py",
+ "data/layer_1.png",
+ "data/layer_2.png",
+ "data/layer_3.png",
+ "data/license.txt",
+ "data/maptexture.jpg",
+ "data/narrowarrow.mesh",
+ "data/oilrig.mesh",
+ "data/pipe.mesh",
+ "data/raindata.txt",
+ "data/refinery.mesh",
+ "data/topography.png"
+]
+}
+++ /dev/null
-Minimal Surface Example
-=======================
-
-The example shows the minimal code to create a surface.
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import sys
-
-from PySide6.QtCore import QSize
-from PySide6.QtGui import QVector3D
-from PySide6.QtGraphs import (Q3DSurface, QSurfaceDataItem,
- QSurface3DSeries)
-from PySide6.QtWidgets import QApplication
-from PySide6.QtQuickWidgets import QQuickWidget
-
-
-DESCRIPTION = """Minimal Qt Graphs Surface Example
-
-Use the mouse wheel to zoom. Rotate using the right mouse button.
-"""
-
-
-if __name__ == '__main__':
- app = QApplication(sys.argv)
-
- print(DESCRIPTION)
-
- surface = Q3DSurface()
- axis = surface.axisX()
- axis.setTitle("X")
- axis.setTitleVisible(True)
- axis = surface.axisY()
- axis.setTitle("Y")
- axis.setTitleVisible(True)
- axis = surface.axisZ()
- axis.setTitle("Z")
- axis.setTitleVisible(True)
-
- data = []
- data_row1 = [QSurfaceDataItem(QVector3D(0, 0.1, 0.5)),
- QSurfaceDataItem(QVector3D(1, 0.5, 0.5))]
- data.append(data_row1)
- data_row2 = [QSurfaceDataItem(QVector3D(0, 1.8, 1)),
- QSurfaceDataItem(QVector3D(1, 1.2, 1))]
- data.append(data_row2)
-
- series = QSurface3DSeries()
- series.dataProxy().resetArray(data)
- surface.addSeries(series)
-
- available_height = app.primaryScreen().availableGeometry().height()
- width = available_height * 4 / 5
- surface.resize(QSize(width, width))
- surface.setResizeMode(QQuickWidget.SizeRootObjectToView)
- surface.show()
-
- sys.exit(app.exec())
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from enum import Enum
-from math import sin, cos, degrees
-
-from PySide6.QtCore import Qt
-from PySide6.QtGraphs import QAbstract3DGraph, Q3DInputHandler
-
-
-class InputState(Enum):
- StateNormal = 0
- StateDraggingX = 1
- StateDraggingZ = 2
- StateDraggingY = 3
-
-
-class AxesInputHandler(Q3DInputHandler):
-
- def __init__(self, graph, parent=None):
- super().__init__(parent)
- self._mousePressed = False
- self._state = InputState.StateNormal
- self._axisX = None
- self._axisZ = None
- self._axisY = None
- self._speedModifier = 15.0
-
- # Connect to the item selection signal from graph
- graph.selectedElementChanged.connect(self.handleElementSelected)
-
- def setAxes(self, axisX, axisZ, axisY):
- self._axisX = axisX
- self._axisZ = axisZ
- self._axisY = axisY
-
- def setDragSpeedModifier(self, modifier):
- self._speedModifier = modifier
-
- def mousePressEvent(self, event, mousePos):
- super().mousePressEvent(event, mousePos)
- if Qt.LeftButton == event.button():
- self._mousePressed = True
-
- def mouseMoveEvent(self, event, mousePos):
- # Check if we're trying to drag axis label
- if self._mousePressed and self._state != InputState.StateNormal:
- self.setPreviousInputPos(self.inputPosition())
- self.setInputPosition(mousePos)
- self.handleAxisDragging()
- else:
- super().mouseMoveEvent(event, mousePos)
-
- def mouseReleaseEvent(self, event, mousePos):
- super().mouseReleaseEvent(event, mousePos)
- self._mousePressed = False
- self._state = InputState.StateNormal
-
- def handleElementSelected(self, type):
- if type == QAbstract3DGraph.ElementAxisXLabel:
- self._state = InputState.StateDraggingX
- elif type == QAbstract3DGraph.ElementAxisYLabel:
- self._state = InputState.StateDraggingY
- elif type == QAbstract3DGraph.ElementAxisZLabel:
- self._state = InputState.StateDraggingZ
- else:
- self._state = InputState.StateNormal
-
- def handleAxisDragging(self):
- distance = 0.0
- # Get scene orientation from active camera
- ac = self.scene().activeCamera()
- xRotation = ac.xRotation()
- yRotation = ac.yRotation()
-
- # Calculate directional drag multipliers based on rotation
- xMulX = cos(degrees(xRotation))
- xMulY = sin(degrees(xRotation))
- zMulX = sin(degrees(xRotation))
- zMulY = cos(degrees(xRotation))
-
- # Get the drag amount
- move = self.inputPosition() - self.previousInputPos()
-
- # Flip the effect of y movement if we're viewing from below
- yMove = -move.y() if yRotation < 0 else move.y()
-
- # Adjust axes
- if self._state == InputState.StateDraggingX:
- distance = (move.x() * xMulX - yMove * xMulY) / self._speedModifier
- self._axisX.setRange(self._axisX.min() - distance,
- self._axisX.max() - distance)
- elif self._state == InputState.StateDraggingZ:
- distance = (move.x() * zMulX + yMove * zMulY) / self._speedModifier
- self._axisZ.setRange(self._axisZ.min() + distance,
- self._axisZ.max() + distance)
- elif self._state == InputState.StateDraggingY:
- # No need to use adjusted y move here
- distance = move.y() / self._speedModifier
- self._axisY.setRange(self._axisY.min() + distance,
- self._axisY.max() + distance)
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from graphmodifier import GraphModifier
-
-from PySide6.QtCore import QObject, Qt
-from PySide6.QtGui import QFont
-from PySide6.QtWidgets import (QButtonGroup, QCheckBox, QComboBox, QFontComboBox,
- QLabel, QPushButton, QHBoxLayout, QSizePolicy,
- QRadioButton, QSlider, QVBoxLayout, QWidget)
-from PySide6.QtQuickWidgets import QQuickWidget
-from PySide6.QtGraphs import (QAbstract3DGraph, QAbstract3DSeries, Q3DBars)
-
-
-class BarGraph(QObject):
-
- def __init__(self, minimum_graph_size, maximum_graph_size):
- super().__init__()
- self._barsGraph = Q3DBars()
- self._barsWidget = QWidget()
- hLayout = QHBoxLayout(self._barsWidget)
- self._barsGraph.setMinimumSize(minimum_graph_size)
- self._barsGraph.setMaximumSize(maximum_graph_size)
- self._barsGraph.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
- self._barsGraph.setFocusPolicy(Qt.StrongFocus)
- self._barsGraph.setResizeMode(QQuickWidget.SizeRootObjectToView)
- hLayout.addWidget(self._barsGraph, 1)
-
- vLayout = QVBoxLayout()
- hLayout.addLayout(vLayout)
-
- themeList = QComboBox(self._barsWidget)
- themeList.addItem("Qt")
- themeList.addItem("Primary Colors")
- themeList.addItem("Digia")
- themeList.addItem("Stone Moss")
- themeList.addItem("Army Blue")
- themeList.addItem("Retro")
- themeList.addItem("Ebony")
- themeList.addItem("Isabelle")
- themeList.setCurrentIndex(0)
-
- labelButton = QPushButton(self._barsWidget)
- labelButton.setText("Change label style")
-
- smoothCheckBox = QCheckBox(self._barsWidget)
- smoothCheckBox.setText("Smooth bars")
- smoothCheckBox.setChecked(False)
-
- barStyleList = QComboBox(self._barsWidget)
- barStyleList.addItem("Bar", QAbstract3DSeries.MeshBar)
- barStyleList.addItem("Pyramid", QAbstract3DSeries.MeshPyramid)
- barStyleList.addItem("Cone", QAbstract3DSeries.MeshCone)
- barStyleList.addItem("Cylinder", QAbstract3DSeries.MeshCylinder)
- barStyleList.addItem("Bevel bar", QAbstract3DSeries.MeshBevelBar)
- barStyleList.addItem("Sphere", QAbstract3DSeries.MeshSphere)
- barStyleList.setCurrentIndex(4)
-
- cameraButton = QPushButton(self._barsWidget)
- cameraButton.setText("Change camera preset")
-
- zoomToSelectedButton = QPushButton(self._barsWidget)
- zoomToSelectedButton.setText("Zoom to selected bar")
-
- selectionModeList = QComboBox(self._barsWidget)
- selectionModeList.addItem("None", QAbstract3DGraph.SelectionNone)
- selectionModeList.addItem("Bar", QAbstract3DGraph.SelectionItem)
- selectionModeList.addItem("Row", QAbstract3DGraph.SelectionRow)
- sel = QAbstract3DGraph.SelectionItemAndRow
- selectionModeList.addItem("Bar and Row", sel)
- selectionModeList.addItem("Column", QAbstract3DGraph.SelectionColumn)
- sel = QAbstract3DGraph.SelectionItemAndColumn
- selectionModeList.addItem("Bar and Column", sel)
- sel = QAbstract3DGraph.SelectionRowAndColumn
- selectionModeList.addItem("Row and Column", sel)
- sel = QAbstract3DGraph.SelectionItemRowAndColumn
- selectionModeList.addItem("Bar, Row and Column", sel)
- sel = QAbstract3DGraph.SelectionSlice | QAbstract3DGraph.SelectionRow
- selectionModeList.addItem("Slice into Row", sel)
- sel = QAbstract3DGraph.SelectionSlice | QAbstract3DGraph.SelectionItemAndRow
- selectionModeList.addItem("Slice into Row and Item", sel)
- sel = QAbstract3DGraph.SelectionSlice | QAbstract3DGraph.SelectionColumn
- selectionModeList.addItem("Slice into Column", sel)
- sel = (QAbstract3DGraph.SelectionSlice
- | QAbstract3DGraph.SelectionItemAndColumn)
- selectionModeList.addItem("Slice into Column and Item", sel)
- sel = (QAbstract3DGraph.SelectionItemRowAndColumn
- | QAbstract3DGraph.SelectionMultiSeries)
- selectionModeList.addItem("Multi: Bar, Row, Col", sel)
- sel = (QAbstract3DGraph.SelectionSlice
- | QAbstract3DGraph.SelectionItemAndRow
- | QAbstract3DGraph.SelectionMultiSeries)
- selectionModeList.addItem("Multi, Slice: Row, Item", sel)
- sel = (QAbstract3DGraph.SelectionSlice
- | QAbstract3DGraph.SelectionItemAndColumn
- | QAbstract3DGraph.SelectionMultiSeries)
- selectionModeList.addItem("Multi, Slice: Col, Item", sel)
- selectionModeList.setCurrentIndex(1)
-
- backgroundCheckBox = QCheckBox(self._barsWidget)
- backgroundCheckBox.setText("Show background")
- backgroundCheckBox.setChecked(False)
-
- gridCheckBox = QCheckBox(self._barsWidget)
- gridCheckBox.setText("Show grid")
- gridCheckBox.setChecked(True)
-
- seriesCheckBox = QCheckBox(self._barsWidget)
- seriesCheckBox.setText("Show second series")
- seriesCheckBox.setChecked(False)
-
- reverseValueAxisCheckBox = QCheckBox(self._barsWidget)
- reverseValueAxisCheckBox.setText("Reverse value axis")
- reverseValueAxisCheckBox.setChecked(False)
-
- reflectionCheckBox = QCheckBox(self._barsWidget)
- reflectionCheckBox.setText("Show reflections")
- reflectionCheckBox.setChecked(False)
-
- rotationSliderX = QSlider(Qt.Horizontal, self._barsWidget)
- rotationSliderX.setTickInterval(30)
- rotationSliderX.setTickPosition(QSlider.TicksBelow)
- rotationSliderX.setMinimum(-180)
- rotationSliderX.setValue(0)
- rotationSliderX.setMaximum(180)
- rotationSliderY = QSlider(Qt.Horizontal, self._barsWidget)
- rotationSliderY.setTickInterval(15)
- rotationSliderY.setTickPosition(QSlider.TicksAbove)
- rotationSliderY.setMinimum(-90)
- rotationSliderY.setValue(0)
- rotationSliderY.setMaximum(90)
-
- fontSizeSlider = QSlider(Qt.Horizontal, self._barsWidget)
- fontSizeSlider.setTickInterval(10)
- fontSizeSlider.setTickPosition(QSlider.TicksBelow)
- fontSizeSlider.setMinimum(1)
- fontSizeSlider.setValue(30)
- fontSizeSlider.setMaximum(100)
-
- fontList = QFontComboBox(self._barsWidget)
- fontList.setCurrentFont(QFont("Times New Roman"))
-
- shadowQuality = QComboBox(self._barsWidget)
- shadowQuality.addItem("None")
- shadowQuality.addItem("Low")
- shadowQuality.addItem("Medium")
- shadowQuality.addItem("High")
- shadowQuality.addItem("Low Soft")
- shadowQuality.addItem("Medium Soft")
- shadowQuality.addItem("High Soft")
- shadowQuality.setCurrentIndex(5)
-
- rangeList = QComboBox(self._barsWidget)
- rangeList.addItem("2015")
- rangeList.addItem("2016")
- rangeList.addItem("2017")
- rangeList.addItem("2018")
- rangeList.addItem("2019")
- rangeList.addItem("2020")
- rangeList.addItem("2021")
- rangeList.addItem("2022")
- rangeList.addItem("All")
- rangeList.setCurrentIndex(8)
-
- axisTitlesVisibleCB = QCheckBox(self._barsWidget)
- axisTitlesVisibleCB.setText("Axis titles visible")
- axisTitlesVisibleCB.setChecked(True)
-
- axisTitlesFixedCB = QCheckBox(self._barsWidget)
- axisTitlesFixedCB.setText("Axis titles fixed")
- axisTitlesFixedCB.setChecked(True)
-
- axisLabelRotationSlider = QSlider(Qt.Horizontal, self._barsWidget)
- axisLabelRotationSlider.setTickInterval(10)
- axisLabelRotationSlider.setTickPosition(QSlider.TicksBelow)
- axisLabelRotationSlider.setMinimum(0)
- axisLabelRotationSlider.setValue(30)
- axisLabelRotationSlider.setMaximum(90)
-
- modeGroup = QButtonGroup(self._barsWidget)
- modeWeather = QRadioButton("Temperature Data", self._barsWidget)
- modeWeather.setChecked(True)
- modeCustomProxy = QRadioButton("Custom Proxy Data", self._barsWidget)
- modeGroup.addButton(modeWeather)
- modeGroup.addButton(modeCustomProxy)
-
- vLayout.addWidget(QLabel("Rotate horizontally"))
- vLayout.addWidget(rotationSliderX, 0, Qt.AlignTop)
- vLayout.addWidget(QLabel("Rotate vertically"))
- vLayout.addWidget(rotationSliderY, 0, Qt.AlignTop)
- vLayout.addWidget(labelButton, 0, Qt.AlignTop)
- vLayout.addWidget(cameraButton, 0, Qt.AlignTop)
- vLayout.addWidget(zoomToSelectedButton, 0, Qt.AlignTop)
- vLayout.addWidget(backgroundCheckBox)
- vLayout.addWidget(gridCheckBox)
- vLayout.addWidget(smoothCheckBox)
- vLayout.addWidget(reflectionCheckBox)
- vLayout.addWidget(seriesCheckBox)
- vLayout.addWidget(reverseValueAxisCheckBox)
- vLayout.addWidget(axisTitlesVisibleCB)
- vLayout.addWidget(axisTitlesFixedCB)
- vLayout.addWidget(QLabel("Show year"))
- vLayout.addWidget(rangeList)
- vLayout.addWidget(QLabel("Change bar style"))
- vLayout.addWidget(barStyleList)
- vLayout.addWidget(QLabel("Change selection mode"))
- vLayout.addWidget(selectionModeList)
- vLayout.addWidget(QLabel("Change theme"))
- vLayout.addWidget(themeList)
- vLayout.addWidget(QLabel("Adjust shadow quality"))
- vLayout.addWidget(shadowQuality)
- vLayout.addWidget(QLabel("Change font"))
- vLayout.addWidget(fontList)
- vLayout.addWidget(QLabel("Adjust font size"))
- vLayout.addWidget(fontSizeSlider)
- vLayout.addWidget(QLabel("Axis label rotation"))
- vLayout.addWidget(axisLabelRotationSlider, 0, Qt.AlignTop)
- vLayout.addWidget(modeWeather, 0, Qt.AlignTop)
- vLayout.addWidget(modeCustomProxy, 1, Qt.AlignTop)
-
- self._modifier = GraphModifier(self._barsGraph, self)
-
- rotationSliderX.valueChanged.connect(self._modifier.rotateX)
- rotationSliderY.valueChanged.connect(self._modifier.rotateY)
-
- labelButton.clicked.connect(self._modifier.changeLabelBackground)
- cameraButton.clicked.connect(self._modifier.changePresetCamera)
- zoomToSelectedButton.clicked.connect(self._modifier.zoomToSelectedBar)
-
- backgroundCheckBox.stateChanged.connect(self._modifier.setBackgroundEnabled)
- gridCheckBox.stateChanged.connect(self._modifier.setGridEnabled)
- smoothCheckBox.stateChanged.connect(self._modifier.setSmoothBars)
- seriesCheckBox.stateChanged.connect(self._modifier.setSeriesVisibility)
- reverseValueAxisCheckBox.stateChanged.connect(self._modifier.setReverseValueAxis)
- reflectionCheckBox.stateChanged.connect(self._modifier.setReflection)
-
- self._modifier.backgroundEnabledChanged.connect(backgroundCheckBox.setChecked)
- self._modifier.gridEnabledChanged.connect(gridCheckBox.setChecked)
-
- rangeList.currentIndexChanged.connect(self._modifier.changeRange)
-
- barStyleList.currentIndexChanged.connect(self._modifier.changeStyle)
-
- selectionModeList.currentIndexChanged.connect(self._modifier.changeSelectionMode)
-
- themeList.currentIndexChanged.connect(self._modifier.changeTheme)
-
- shadowQuality.currentIndexChanged.connect(self._modifier.changeShadowQuality)
-
- self._modifier.shadowQualityChanged.connect(shadowQuality.setCurrentIndex)
- self._barsGraph.shadowQualityChanged.connect(self._modifier.shadowQualityUpdatedByVisual)
-
- fontSizeSlider.valueChanged.connect(self._modifier.changeFontSize)
- fontList.currentFontChanged.connect(self._modifier.changeFont)
-
- self._modifier.fontSizeChanged.connect(fontSizeSlider.setValue)
- self._modifier.fontChanged.connect(fontList.setCurrentFont)
-
- axisTitlesVisibleCB.stateChanged.connect(self._modifier.setAxisTitleVisibility)
- axisTitlesFixedCB.stateChanged.connect(self._modifier.setAxisTitleFixed)
- axisLabelRotationSlider.valueChanged.connect(self._modifier.changeLabelRotation)
-
- modeWeather.toggled.connect(self._modifier.setDataModeToWeather)
- modeCustomProxy.toggled.connect(self._modifier.setDataModeToCustom)
- modeWeather.toggled.connect(seriesCheckBox.setEnabled)
- modeWeather.toggled.connect(rangeList.setEnabled)
- modeWeather.toggled.connect(axisTitlesVisibleCB.setEnabled)
- modeWeather.toggled.connect(axisTitlesFixedCB.setEnabled)
- modeWeather.toggled.connect(axisLabelRotationSlider.setEnabled)
-
- def barsWidget(self):
- return self._barsWidget
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from enum import Enum
-from math import sin, cos, degrees
-
-from PySide6.QtCore import Qt
-from PySide6.QtGraphs import (QAbstract3DGraph, Q3DInputHandler)
-
-
-class InputState(Enum):
- StateNormal = 0
- StateDraggingX = 1
- StateDraggingZ = 2
- StateDraggingY = 3
-
-
-class CustomInputHandler(Q3DInputHandler):
-
- def __init__(self, graph, parent=None):
- super().__init__(parent)
- self._highlight = None
- self._mousePressed = False
- self._state = InputState.StateNormal
- self._axisX = None
- self._axisY = None
- self._axisZ = None
- self._speedModifier = 20.0
- self._aspectRatio = 0.0
- self._axisXMinValue = 0.0
- self._axisXMaxValue = 0.0
- self._axisXMinRange = 0.0
- self._axisZMinValue = 0.0
- self._axisZMaxValue = 0.0
- self._axisZMinRange = 0.0
- self._areaMinValue = 0.0
- self._areaMaxValue = 0.0
-
- # Connect to the item selection signal from graph
- graph.selectedElementChanged.connect(self.handleElementSelected)
-
- def setAspectRatio(self, ratio):
- self._aspectRatio = ratio
-
- def setHighlightSeries(self, series):
- self._highlight = series
-
- def setDragSpeedModifier(self, modifier):
- self._speedModifier = modifier
-
- def setLimits(self, min, max, minRange):
- self._areaMinValue = min
- self._areaMaxValue = max
- self._axisXMinValue = self._areaMinValue
- self._axisXMaxValue = self._areaMaxValue
- self._axisZMinValue = self._areaMinValue
- self._axisZMaxValue = self._areaMaxValue
- self._axisXMinRange = minRange
- self._axisZMinRange = minRange
-
- def setAxes(self, axisX, axisY, axisZ):
- self._axisX = axisX
- self._axisY = axisY
- self._axisZ = axisZ
-
- def mousePressEvent(self, event, mousePos):
- if Qt.LeftButton == event.button():
- self._highlight.setVisible(False)
- self._mousePressed = True
- super().mousePressEvent(event, mousePos)
-
- def wheelEvent(self, event):
- delta = float(event.angleDelta().y())
-
- self._axisXMinValue += delta
- self._axisXMaxValue -= delta
- self._axisZMinValue += delta
- self._axisZMaxValue -= delta
- self.checkConstraints()
-
- y = (self._axisXMaxValue - self._axisXMinValue) * self._aspectRatio
-
- self._axisX.setRange(self._axisXMinValue, self._axisXMaxValue)
- self._axisY.setRange(100.0, y)
- self._axisZ.setRange(self._axisZMinValue, self._axisZMaxValue)
-
- def mouseMoveEvent(self, event, mousePos):
- # Check if we're trying to drag axis label
- if self._mousePressed and self._state != InputState.StateNormal:
- self.setPreviousInputPos(self.inputPosition())
- self.setInputPosition(mousePos)
- self.handleAxisDragging()
- else:
- super().mouseMoveEvent(event, mousePos)
-
- def mouseReleaseEvent(self, event, mousePos):
- super().mouseReleaseEvent(event, mousePos)
- self._mousePressed = False
- self._state = InputState.StateNormal
-
- def handleElementSelected(self, type):
- if type == QAbstract3DGraph.ElementAxisXLabel:
- self._state = InputState.StateDraggingX
- elif type == QAbstract3DGraph.ElementAxisZLabel:
- self._state = InputState.StateDraggingZ
- else:
- self._state = InputState.StateNormal
-
- def handleAxisDragging(self):
- distance = 0.0
-
- # Get scene orientation from active camera
- xRotation = self.scene().activeCamera().xRotation()
-
- # Calculate directional drag multipliers based on rotation
- xMulX = cos(degrees(xRotation))
- xMulY = sin(degrees(xRotation))
- zMulX = xMulY
- zMulY = xMulX
-
- # Get the drag amount
- move = self.inputPosition() - self.previousInputPos()
-
- # Adjust axes
- if self._state == InputState.StateDraggingX:
- distance = (move.x() * xMulX - move.y() * xMulY) * self._speedModifier
- self._axisXMinValue -= distance
- self._axisXMaxValue -= distance
- if self._axisXMinValue < self._areaMinValue:
- dist = self._axisXMaxValue - self._axisXMinValue
- self._axisXMinValue = self._areaMinValue
- self._axisXMaxValue = self._axisXMinValue + dist
-
- if self._axisXMaxValue > self._areaMaxValue:
- dist = self._axisXMaxValue - self._axisXMinValue
- self._axisXMaxValue = self._areaMaxValue
- self._axisXMinValue = self._axisXMaxValue - dist
-
- self._axisX.setRange(self._axisXMinValue, self._axisXMaxValue)
- elif self._state == InputState.StateDraggingZ:
- distance = (move.x() * zMulX + move.y() * zMulY) * self._speedModifier
- self._axisZMinValue += distance
- self._axisZMaxValue += distance
- if self._axisZMinValue < self._areaMinValue:
- dist = self._axisZMaxValue - self._axisZMinValue
- self._axisZMinValue = self._areaMinValue
- self._axisZMaxValue = self._axisZMinValue + dist
-
- if self._axisZMaxValue > self._areaMaxValue:
- dist = self._axisZMaxValue - self._axisZMinValue
- self._axisZMaxValue = self._areaMaxValue
- self._axisZMinValue = self._axisZMaxValue - dist
-
- self._axisZ.setRange(self._axisZMinValue, self._axisZMaxValue)
-
- def checkConstraints(self):
- if self._axisXMinValue < self._areaMinValue:
- self._axisXMinValue = self._areaMinValue
- if self._axisXMaxValue > self._areaMaxValue:
- self._axisXMaxValue = self._areaMaxValue
- # Don't allow too much zoom in
- range = self._axisXMaxValue - self._axisXMinValue
- if range < self._axisXMinRange:
- adjust = (self._axisXMinRange - range) / 2.0
- self._axisXMinValue -= adjust
- self._axisXMaxValue += adjust
-
- if self._axisZMinValue < self._areaMinValue:
- self._axisZMinValue = self._areaMinValue
- if self._axisZMaxValue > self._areaMaxValue:
- self._axisZMaxValue = self._areaMaxValue
- # Don't allow too much zoom in
- range = self._axisZMaxValue - self._axisZMinValue
- if range < self._axisZMinRange:
- adjust = (self._axisZMinRange - range) / 2.0
- self._axisZMinValue -= adjust
- self._axisZMaxValue += adjust
+++ /dev/null
-License information regarding the data obtained from National Land Survey of
-Finland http://www.maanmittauslaitos.fi/en
-- topographic model from Elevation model 2 m (U4421B, U4421D, U4422A and
- U4422C) 08/2014
-- map image extracted from Topographic map raster 1:50 000 (U442) 08/2014
-
-National Land Survey open data licence - version 1.0 - 1 May 2012
-
-1. General information
-
-The National Land Survey of Finland (hereinafter the Licensor), as the holder
-of the immaterial rights to the data, has granted on the terms mentioned below
-the right to use a copy (hereinafter data or dataset(s)) of the data (or a part
-of it).
-
-The Licensee is a natural or legal person who makes use of the data covered by
-this licence. The Licensee accepts the terms of this licence by receiving the
-dataset(s) covered by the licence.
-
-This Licence agreement does not create a co-operation or business relationship
-between the Licensee and the Licensor.
-
-2. Terms of the licence
-
-2.1. Right of use
-
-This licence grants a worldwide, free of charge and irrevocable parallel right
-of use to open data. According to the terms of the licence, data received by
-the Licensee can be freely:
- - copied, distributed and published,
- - modified and utilised commercially and non-commercially,
- - inserted into other products and
- - used as a part of a software application or service.
-
-2.2. Duties and responsibilities of the Licensee
-
-Through reasonable means suitable to the distribution medium or method which is
-used in conjunction with a product containing data or a service utilising data
-covered by this licence or while distributing data, the Licensee shall:
- - mention the name of the Licensor, the name of the dataset(s) and the time
- when the National Land Survey has delivered the dataset(s) (e.g.: contains
- data from the National Land Survey of Finland Topographic Database 06/2012)
- - provide a copy of this licence or a link to it, as well as
- - require third parties to provide the same information when granting rights
- to copies of dataset(s) or products and services containing such data and
- - remove the name of the Licensor from the product or service, if required to
- do so by the Licensor.
-
-The terms of this licence do not allow the Licensee to state in conjunction
-with the use of dataset(s) that the Licensor supports or recommends such use.
-
-2.3. Duties and responsibilities of the Licensor
-
-The Licensor shall ensure that
- - the Licensor has the right to grant rights to the dataset(s) in accordance
- with this licence.
-
-The data has been licensed "as is" and the Licensor
- - shall not be held responsible for any errors or omissions in the data,
- disclaims any warranty for the validity or up to date status of the data and
- shall be free from liability for direct or consequential damages arising
- from the use of data provided by the Licensor,
- - and is not obligated to ensure the continuous availability of the data, nor
- to announce in advance the interruption or cessation of availability, and
- the Licensor shall be free from liability for direct or consequential
- damages arising from any such interruption or cessation.
-
-3. Jurisdiction
-
-Finnish law shall apply to this licence.
-
-4. Changes to this licence
-
-The Licensor may at any time change the terms of the licence or apply a
-different licence to the data. The terms of this licence shall, however, still
-apply to such data that has been received prior to the change of the terms of
-the licence or the licence itself.
+++ /dev/null
-# Rainfall per month from 2010 to 2022 in Northern Finland (Oulu)
-# Format: year, month, rainfall
-2010,1, 0,
-2010,2, 3.4,
-2010,3, 52,
-2010,4, 33.8,
-2010,5, 45.6,
-2010,6, 43.8,
-2010,7, 104.6,
-2010,8, 105.4,
-2010,9, 107.2,
-2010,10,38.6,
-2010,11,17.8,
-2010,12,0,
-2011,1, 8.2,
-2011,2, 1.6,
-2011,3, 27.4,
-2011,4, 15.8,
-2011,5, 57.6,
-2011,6, 85.2,
-2011,7, 127,
-2011,8, 72.2,
-2011,9, 82.2,
-2011,10,62.4,
-2011,11,31.6,
-2011,12,53.8,
-2012,1, 0,
-2012,2, 5,
-2012,3, 32.4,
-2012,4, 57.6,
-2012,5, 71.4,
-2012,6, 60.8,
-2012,7, 109,
-2012,8, 43.6,
-2012,9, 79.4,
-2012,10,117.2,
-2012,11,59,
-2012,12,0.2,
-2013,1, 28,
-2013,2, 19,
-2013,3, 0,
-2013,4, 37.6,
-2013,5, 44.2,
-2013,6, 104.8,
-2013,7, 84.2,
-2013,8, 57.2,
-2013,9, 37.2,
-2013,10,64.6,
-2013,11,77.8,
-2013,12,92.8,
-2014,1, 23.8,
-2014,2, 23.6,
-2014,3, 15.4,
-2014,4, 13.2,
-2014,5, 36.4,
-2014,6, 26.4,
-2014,7, 95.8,
-2014,8, 81.8,
-2014,9, 13.8,
-2014,10,94.6,
-2014,11,44.6,
-2014,12,31,
-2015,1, 37.4,
-2015,2, 21,
-2015,3, 42,
-2015,4, 8.8,
-2015,5, 82.4,
-2015,6, 150,
-2015,7, 56.8,
-2015,8, 67.2,
-2015,9, 131.2,
-2015,10,38.4,
-2015,11,83.4,
-2015,12,47.8,
-2016,1, 12.4,
-2016,2, 34.8,
-2016,3, 29,
-2016,4, 40.4,
-2016,5, 32.4,
-2016,6, 80.2,
-2016,7, 102.6,
-2016,8, 95.6,
-2016,9, 40.2,
-2016,10,7.8,
-2016,11,39.6,
-2016,12,8.8,
-2017,1, 9.4,
-2017,2, 6.6,
-2017,3, 29,
-2017,4, 46.2,
-2017,5, 43.2,
-2017,6, 25.2,
-2017,7, 72.4,
-2017,8, 58.8,
-2017,9, 68.8,
-2017,10,45.8,
-2017,11,36.8,
-2017,12,29.6,
-2018,1, 19.8,
-2018,2, 0.8,
-2018,3, 4,
-2018,4, 23.2,
-2018,5, 13.2,
-2018,6, 62.8,
-2018,7, 33,
-2018,8, 96.6,
-2018,9, 72.6,
-2018,10,48.8,
-2018,11,31.8,
-2018,12,12.8,
-2019,1, 0.2,
-2019,2, 24.8,
-2019,3, 32,
-2019,4, 8.8,
-2019,5, 71.4,
-2019,6, 65.8,
-2019,7, 17.6,
-2019,8, 90,
-2019,9, 50,
-2019,10,77,
-2019,11,27,
-2019,12,43.2,
-2020,1, 28.8,
-2020,2, 45,
-2020,3, 18.6,
-2020,4, 13,
-2020,5, 30.8,
-2020,6, 21.4,
-2020,7, 163.6,
-2020,8, 12,
-2020,9, 102.4,
-2020,10,133.2,
-2020,11,69.8,
-2020,12,40.6,
-2021,1, 0.4,
-2021,2, 21.6,
-2021,3, 24,
-2021,4, 51.4,
-2021,5, 76.4,
-2021,6, 29.2,
-2021,7, 36.4,
-2021,8, 116,
-2021,9, 72.4,
-2021,10,93.4,
-2021,11,21,
-2021,12,10.2,
-2022,1, 8.6,
-2022,2, 6.6,
-2022,3, 5.2,
-2022,4, 15.2,
-2022,5, 37.6,
-2022,6, 45,
-2022,7, 67.4,
-2022,8, 161.6,
-2022,9, 22.8,
-2022,10,75.2,
-2022,11,21.8,
-2022,12,0.2
+++ /dev/null
-Widget Gallery
-==============
-
-
-Widget Gallery demonstrates all three graph types and some of their special
-features. The graphs have their own tabs in the application.
-
-
-.. image:: widgetgallery.webp
- :width: 400
- :alt: Widget Screenshot
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-
-from math import atan, degrees
-import numpy as np
-
-from PySide6.QtCore import QObject, QPropertyAnimation, Signal, Slot
-from PySide6.QtGui import QFont, QVector3D
-from PySide6.QtGraphs import (QAbstract3DGraph, QAbstract3DSeries,
- QBarDataItem, QBar3DSeries, QCategory3DAxis,
- QValue3DAxis, Q3DCamera, Q3DTheme)
-
-from rainfalldata import RainfallData
-
-# Set up data
-TEMP_OULU = np.array([
- [-7.4, -2.4, 0.0, 3.0, 8.2, 11.6, 14.7, 15.4, 11.4, 4.2, 2.1, -2.3], # 2015
- [-13.4, -3.9, -1.8, 3.1, 10.6, 13.7, 17.8, 13.6, 10.7, 3.5, -3.1, -4.2], # 2016
- [-5.7, -6.7, -3.0, -0.1, 4.7, 12.4, 16.1, 14.1, 9.4, 3.0, -0.3, -3.2], # 2017
- [-6.4, -11.9, -7.4, 1.9, 11.4, 12.4, 21.5, 16.1, 11.0, 4.4, 2.1, -4.1], # 2018
- [-11.7, -6.1, -2.4, 3.9, 7.2, 14.5, 15.6, 14.4, 8.5, 2.0, -3.0, -1.5], # 2019
- [-2.1, -3.4, -1.8, 0.6, 7.0, 17.1, 15.6, 15.4, 11.1, 5.6, 1.9, -1.7], # 2020
- [-9.6, -11.6, -3.2, 2.4, 7.8, 17.3, 19.4, 14.2, 8.0, 5.2, -2.2, -8.6], # 2021
- [-7.3, -6.4, -1.8, 1.3, 8.1, 15.5, 17.6, 17.6, 9.1, 5.4, -1.5, -4.4]], # 2022
- np.float64)
-
-
-TEMP_HELSINKI = np.array([
- [-2.0, -0.1, 1.8, 5.1, 9.7, 13.7, 16.3, 17.3, 12.7, 5.4, 4.6, 2.1], # 2015
- [-10.3, -0.6, 0.0, 4.9, 14.3, 15.7, 17.7, 16.0, 12.7, 4.6, -1.0, -0.9], # 2016
- [-2.9, -3.3, 0.7, 2.3, 9.9, 13.8, 16.1, 15.9, 11.4, 5.0, 2.7, 0.7], # 2017
- [-2.2, -8.4, -4.7, 5.0, 15.3, 15.8, 21.2, 18.2, 13.3, 6.7, 2.8, -2.0], # 2018
- [-6.2, -0.5, -0.3, 6.8, 10.6, 17.9, 17.5, 16.8, 11.3, 5.2, 1.8, 1.4], # 2019
- [1.9, 0.5, 1.7, 4.5, 9.5, 18.4, 16.5, 16.8, 13.0, 8.2, 4.4, 0.9], # 2020
- [-4.7, -8.1, -0.9, 4.5, 10.4, 19.2, 20.9, 15.4, 9.5, 8.0, 1.5, -6.7], # 2021
- [-3.3, -2.2, -0.2, 3.3, 9.6, 16.9, 18.1, 18.9, 9.2, 7.6, 2.3, -3.4]], # 2022
- np.float64)
-
-
-class GraphModifier(QObject):
-
- shadowQualityChanged = Signal(int)
- backgroundEnabledChanged = Signal(bool)
- gridEnabledChanged = Signal(bool)
- fontChanged = Signal(QFont)
- fontSizeChanged = Signal(int)
-
- def __init__(self, bargraph, parent):
- super().__init__(parent)
- self._graph = bargraph
- self._temperatureAxis = QValue3DAxis()
- self._yearAxis = QCategory3DAxis()
- self._monthAxis = QCategory3DAxis()
- self._primarySeries = QBar3DSeries()
- self._secondarySeries = QBar3DSeries()
- self._celsiusString = "°C"
-
- self._xRotation = float(0)
- self._yRotation = float(0)
- self._fontSize = 30
- self._segments = 4
- self._subSegments = 3
- self._minval = float(-20)
- self._maxval = float(20)
- self._barMesh = QAbstract3DSeries.MeshBevelBar
- self._smooth = False
- self._animationCameraX = QPropertyAnimation()
- self._animationCameraY = QPropertyAnimation()
- self._animationCameraZoom = QPropertyAnimation()
- self._animationCameraTarget = QPropertyAnimation()
- self._defaultAngleX = float(0)
- self._defaultAngleY = float(0)
- self._defaultZoom = float(0)
- self._defaultTarget = []
- self._customData = None
-
- self._graph.setShadowQuality(QAbstract3DGraph.ShadowQualitySoftMedium)
- theme = self._graph.activeTheme()
- theme.setBackgroundEnabled(False)
- theme.setFont(QFont("Times New Roman", self._fontSize))
- theme.setLabelBackgroundEnabled(True)
- self._graph.setMultiSeriesUniform(True)
-
- self._months = ["January", "February", "March", "April", "May", "June",
- "July", "August", "September", "October", "November",
- "December"]
- self._years = ["2015", "2016", "2017", "2018", "2019", "2020",
- "2021", "2022"]
-
- self._temperatureAxis.setTitle("Average temperature")
- self._temperatureAxis.setSegmentCount(self._segments)
- self._temperatureAxis.setSubSegmentCount(self._subSegments)
- self._temperatureAxis.setRange(self._minval, self._maxval)
- self._temperatureAxis.setLabelFormat("%.1f " + self._celsiusString)
- self._temperatureAxis.setLabelAutoRotation(30.0)
- self._temperatureAxis.setTitleVisible(True)
-
- self._yearAxis.setTitle("Year")
- self._yearAxis.setLabelAutoRotation(30.0)
- self._yearAxis.setTitleVisible(True)
- self._monthAxis.setTitle("Month")
- self._monthAxis.setLabelAutoRotation(30.0)
- self._monthAxis.setTitleVisible(True)
-
- self._graph.setValueAxis(self._temperatureAxis)
- self._graph.setRowAxis(self._yearAxis)
- self._graph.setColumnAxis(self._monthAxis)
-
- format = "Oulu - @colLabel @rowLabel: @valueLabel"
- self._primarySeries.setItemLabelFormat(format)
- self._primarySeries.setMesh(QAbstract3DSeries.MeshBevelBar)
- self._primarySeries.setMeshSmooth(False)
-
- format = "Helsinki - @colLabel @rowLabel: @valueLabel"
- self._secondarySeries.setItemLabelFormat(format)
- self._secondarySeries.setMesh(QAbstract3DSeries.MeshBevelBar)
- self._secondarySeries.setMeshSmooth(False)
- self._secondarySeries.setVisible(False)
-
- self._graph.addSeries(self._primarySeries)
- self._graph.addSeries(self._secondarySeries)
-
- self.changePresetCamera()
-
- self.resetTemperatureData()
-
- # Set up property animations for zooming to the selected bar
- camera = self._graph.scene().activeCamera()
- self._defaultAngleX = camera.xRotation()
- self._defaultAngleY = camera.yRotation()
- self._defaultZoom = camera.zoomLevel()
- self._defaultTarget = camera.target()
-
- self._animationCameraX.setTargetObject(camera)
- self._animationCameraY.setTargetObject(camera)
- self._animationCameraZoom.setTargetObject(camera)
- self._animationCameraTarget.setTargetObject(camera)
-
- self._animationCameraX.setPropertyName(b"xRotation")
- self._animationCameraY.setPropertyName(b"yRotation")
- self._animationCameraZoom.setPropertyName(b"zoomLevel")
- self._animationCameraTarget.setPropertyName(b"target")
-
- duration = 1700
- self._animationCameraX.setDuration(duration)
- self._animationCameraY.setDuration(duration)
- self._animationCameraZoom.setDuration(duration)
- self._animationCameraTarget.setDuration(duration)
-
- # The zoom always first zooms out above the graph and then zooms in
- zoomOutFraction = 0.3
- self._animationCameraX.setKeyValueAt(zoomOutFraction, 0.0)
- self._animationCameraY.setKeyValueAt(zoomOutFraction, 90.0)
- self._animationCameraZoom.setKeyValueAt(zoomOutFraction, 50.0)
- self._animationCameraTarget.setKeyValueAt(zoomOutFraction,
- QVector3D(0, 0, 0))
- self._customData = RainfallData()
-
- def resetTemperatureData(self):
- # Create data arrays
- dataSet = []
- dataSet2 = []
-
- for year in range(0, len(self._years)):
- # Create a data row
- dataRow = []
- dataRow2 = []
- for month in range(0, len(self._months)):
- # Add data to the row
- item = QBarDataItem()
- item.setValue(TEMP_OULU[year][month])
- dataRow.append(item)
- item = QBarDataItem()
- item.setValue(TEMP_HELSINKI[year][month])
- dataRow2.append(item)
-
- # Add the row to the set
- dataSet.append(dataRow)
- dataSet2.append(dataRow2)
-
- # Add data to the data proxy (the data proxy assumes ownership of it)
- self._primarySeries.dataProxy().resetArray(dataSet, self._years, self._months)
- self._secondarySeries.dataProxy().resetArray(dataSet2, self._years, self._months)
-
- @Slot(int)
- def changeRange(self, range):
- if range >= len(self._years):
- self._yearAxis.setRange(0, len(self._years) - 1)
- else:
- self._yearAxis.setRange(range, range)
-
- @Slot(int)
- def changeStyle(self, style):
- comboBox = self.sender()
- if comboBox:
- self._barMesh = comboBox.itemData(style)
- self._primarySeries.setMesh(self._barMesh)
- self._secondarySeries.setMesh(self._barMesh)
- self._customData.customSeries().setMesh(self._barMesh)
-
- def changePresetCamera(self):
- self._animationCameraX.stop()
- self._animationCameraY.stop()
- self._animationCameraZoom.stop()
- self._animationCameraTarget.stop()
-
- # Restore camera target in case animation has changed it
- self._graph.scene().activeCamera().setTarget(QVector3D(0.0, 0.0, 0.0))
-
- self._preset = Q3DCamera.CameraPresetFront.value
-
- camera = self._graph.scene().activeCamera()
- camera.setCameraPreset(Q3DCamera.CameraPreset(self._preset))
-
- self._preset += 1
- if self._preset > Q3DCamera.CameraPresetDirectlyBelow.value:
- self._preset = Q3DCamera.CameraPresetFrontLow.value
-
- @Slot(int)
- def changeTheme(self, theme):
- currentTheme = self._graph.activeTheme()
- currentTheme.setType(Q3DTheme.Theme(theme))
- self.backgroundEnabledChanged.emit(currentTheme.isBackgroundEnabled())
- self.gridEnabledChanged.emit(currentTheme.isGridEnabled())
- self.fontChanged.emit(currentTheme.font())
- self.fontSizeChanged.emit(currentTheme.font().pointSize())
-
- def changeLabelBackground(self):
- theme = self._graph.activeTheme()
- theme.setLabelBackgroundEnabled(not theme.isLabelBackgroundEnabled())
-
- @Slot(int)
- def changeSelectionMode(self, selectionMode):
- comboBox = self.sender()
- if comboBox:
- flags = comboBox.itemData(selectionMode)
- self._graph.setSelectionMode(QAbstract3DGraph.SelectionFlags(flags))
-
- def changeFont(self, font):
- newFont = font
- self._graph.activeTheme().setFont(newFont)
-
- def changeFontSize(self, fontsize):
- self._fontSize = fontsize
- font = self._graph.activeTheme().font()
- font.setPointSize(self._fontSize)
- self._graph.activeTheme().setFont(font)
-
- @Slot(QAbstract3DGraph.ShadowQuality)
- def shadowQualityUpdatedByVisual(self, sq):
- # Updates the UI component to show correct shadow quality
- self.shadowQualityChanged.emit(sq.value)
-
- @Slot(int)
- def changeLabelRotation(self, rotation):
- self._temperatureAxis.setLabelAutoRotation(float(rotation))
- self._monthAxis.setLabelAutoRotation(float(rotation))
- self._yearAxis.setLabelAutoRotation(float(rotation))
-
- @Slot(bool)
- def setAxisTitleVisibility(self, enabled):
- self._temperatureAxis.setTitleVisible(enabled)
- self._monthAxis.setTitleVisible(enabled)
- self._yearAxis.setTitleVisible(enabled)
-
- @Slot(bool)
- def setAxisTitleFixed(self, enabled):
- self._temperatureAxis.setTitleFixed(enabled)
- self._monthAxis.setTitleFixed(enabled)
- self._yearAxis.setTitleFixed(enabled)
-
- @Slot()
- def zoomToSelectedBar(self):
- self._animationCameraX.stop()
- self._animationCameraY.stop()
- self._animationCameraZoom.stop()
- self._animationCameraTarget.stop()
-
- camera = self._graph.scene().activeCamera()
- currentX = camera.xRotation()
- currentY = camera.yRotation()
- currentZoom = camera.zoomLevel()
- currentTarget = camera.target()
-
- self._animationCameraX.setStartValue(currentX)
- self._animationCameraY.setStartValue(currentY)
- self._animationCameraZoom.setStartValue(currentZoom)
- self._animationCameraTarget.setStartValue(currentTarget)
-
- selectedBar = (self._graph.selectedSeries().selectedBar()
- if self._graph.selectedSeries()
- else QBar3DSeries.invalidSelectionPosition())
-
- if selectedBar != QBar3DSeries.invalidSelectionPosition():
- # Normalize selected bar position within axis range to determine
- # target coordinates
- endTarget = QVector3D()
- xMin = self._graph.columnAxis().min()
- xRange = self._graph.columnAxis().max() - xMin
- zMin = self._graph.rowAxis().min()
- zRange = self._graph.rowAxis().max() - zMin
- endTarget.setX((selectedBar.y() - xMin) / xRange * 2.0 - 1.0)
- endTarget.setZ((selectedBar.x() - zMin) / zRange * 2.0 - 1.0)
-
- # Rotate the camera so that it always points approximately to the
- # graph center
- endAngleX = 90.0 - degrees(atan(float(endTarget.z() / endTarget.x())))
- if endTarget.x() > 0.0:
- endAngleX -= 180.0
- proxy = self._graph.selectedSeries().dataProxy()
- barValue = proxy.itemAt(selectedBar.x(), selectedBar.y()).value()
- endAngleY = 30.0 if barValue >= 0.0 else -30.0
- if self._graph.valueAxis().reversed():
- endAngleY *= -1.0
-
- self._animationCameraX.setEndValue(float(endAngleX))
- self._animationCameraY.setEndValue(endAngleY)
- self._animationCameraZoom.setEndValue(250)
- self._animationCameraTarget.setEndValue(endTarget)
- else:
- # No selected bar, so return to the default view
- self._animationCameraX.setEndValue(self._defaultAngleX)
- self._animationCameraY.setEndValue(self._defaultAngleY)
- self._animationCameraZoom.setEndValue(self._defaultZoom)
- self._animationCameraTarget.setEndValue(self._defaultTarget)
-
- self._animationCameraX.start()
- self._animationCameraY.start()
- self._animationCameraZoom.start()
- self._animationCameraTarget.start()
-
- @Slot(bool)
- def setDataModeToWeather(self, enabled):
- if enabled:
- self.changeDataMode(False)
-
- @Slot(bool)
- def setDataModeToCustom(self, enabled):
- if enabled:
- self.changeDataMode(True)
-
- def changeShadowQuality(self, quality):
- sq = QAbstract3DGraph.ShadowQuality(quality)
- self._graph.setShadowQuality(sq)
- self.shadowQualityChanged.emit(quality)
-
- def rotateX(self, rotation):
- self._xRotation = rotation
- camera = self._graph.scene().activeCamera()
- camera.setCameraPosition(self._xRotation, self._yRotation)
-
- def rotateY(self, rotation):
- self._yRotation = rotation
- camera = self._graph.scene().activeCamera()
- camera.setCameraPosition(self._xRotation, self._yRotation)
-
- def setBackgroundEnabled(self, enabled):
- self._graph.activeTheme().setBackgroundEnabled(bool(enabled))
-
- def setGridEnabled(self, enabled):
- self._graph.activeTheme().setGridEnabled(bool(enabled))
-
- def setSmoothBars(self, smooth):
- self._smooth = bool(smooth)
- self._primarySeries.setMeshSmooth(self._smooth)
- self._secondarySeries.setMeshSmooth(self._smooth)
- self._customData.customSeries().setMeshSmooth(self._smooth)
-
- def setSeriesVisibility(self, enabled):
- self._secondarySeries.setVisible(bool(enabled))
-
- def setReverseValueAxis(self, enabled):
- self._graph.valueAxis().setReversed(enabled)
-
- def setReflection(self, enabled):
- self._graph.setReflection(enabled)
-
- def changeDataMode(self, customData):
- # Change between weather data and data from custom proxy
- if customData:
- self._graph.removeSeries(self._primarySeries)
- self._graph.removeSeries(self._secondarySeries)
- self._graph.addSeries(self._customData.customSeries())
- self._graph.setValueAxis(self._customData.valueAxis())
- self._graph.setRowAxis(self._customData.rowAxis())
- self._graph.setColumnAxis(self._customData.colAxis())
- else:
- self._graph.removeSeries(self._customData.customSeries())
- self._graph.addSeries(self._primarySeries)
- self._graph.addSeries(self._secondarySeries)
- self._graph.setValueAxis(self._temperatureAxis)
- self._graph.setRowAxis(self._yearAxis)
- self._graph.setColumnAxis(self._monthAxis)
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from PySide6.QtCore import QPoint, Qt, Slot
-from PySide6.QtGui import QLinearGradient, QVector3D
-from PySide6.QtGraphs import (QSurface3DSeries, QSurfaceDataItem, Q3DTheme)
-
-
-DARK_RED_POS = 1.0
-RED_POS = 0.8
-YELLOW_POS = 0.6
-GREEN_POS = 0.4
-DARK_GREEN_POS = 0.2
-
-
-class HighlightSeries(QSurface3DSeries):
-
- def __init__(self):
- super().__init__()
- self._width = 100
- self._height = 100
- self._srcWidth = 0
- self._srcHeight = 0
- self._position = {}
- self._topographicSeries = None
- self._minHeight = 0.0
- self.setDrawMode(QSurface3DSeries.DrawSurface)
- self.setFlatShadingEnabled(True)
- self.setVisible(False)
-
- def setTopographicSeries(self, series):
- self._topographicSeries = series
- array = self._topographicSeries.dataProxy().array()
- self._srcWidth = len(array[0])
- self._srcHeight = len(array)
- self._topographicSeries.selectedPointChanged.connect(self.handlePositionChange)
-
- def setMinHeight(self, height):
- self. m_minHeight = height
-
- @Slot(QPoint)
- def handlePositionChange(self, position):
- self._position = position
-
- if position == self.invalidSelectionPosition():
- self.setVisible(False)
- return
-
- halfWidth = self._width / 2
- halfHeight = self._height / 2
-
- startX = position.y() - halfWidth
- if startX < 0:
- startX = 0
- endX = position.y() + halfWidth
- if endX > (self._srcWidth - 1):
- endX = self._srcWidth - 1
- startZ = position.x() - halfHeight
- if startZ < 0:
- startZ = 0
- endZ = position.x() + halfHeight
- if endZ > (self._srcHeight - 1):
- endZ = self._srcHeight - 1
-
- srcProxy = self._topographicSeries.dataProxy()
- srcArray = srcProxy.array()
-
- dataArray = []
- for i in range(int(startZ), int(endZ)):
- newRow = []
- srcRow = srcArray[i]
- for j in range(startX, endX):
- pos = srcRow.at(j).position()
- pos.setY(pos.y() + 0.1)
- item = QSurfaceDataItem(QVector3D(pos))
- newRow.append(item)
- dataArray.append(newRow)
- self.dataProxy().resetArray(dataArray)
- self.setVisible(True)
-
- @Slot(float)
- def handleGradientChange(self, value):
- ratio = self._minHeight / value
-
- gr = QLinearGradient()
- gr.setColorAt(0.0, Qt.black)
- gr.setColorAt(DARK_GREEN_POS * ratio, Qt.darkGreen)
- gr.setColorAt(GREEN_POS * ratio, Qt.green)
- gr.setColorAt(YELLOW_POS * ratio, Qt.yellow)
- gr.setColorAt(RED_POS * ratio, Qt.red)
- gr.setColorAt(DARK_RED_POS * ratio, Qt.darkRed)
-
- self.setBaseGradient(gr)
- self.setColorStyle(Q3DTheme.ColorStyleRangeGradient)
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-"""PySide6 port of the Qt Graphs widgetgallery example from Qt v6.x"""
-
-import sys
-
-from PySide6.QtCore import QSize
-from PySide6.QtWidgets import QApplication, QTabWidget
-
-from bargraph import BarGraph
-from scattergraph import ScatterGraph
-from surfacegraph import SurfaceGraph
-
-
-if __name__ == "__main__":
- app = QApplication(sys.argv)
-
- # Create a tab widget for creating own tabs for Q3DBars, Q3DScatter, and Q3DSurface
- tabWidget = QTabWidget()
- tabWidget.setWindowTitle("Widget Gallery")
-
- screen_size = tabWidget.screen().size()
- minimum_graph_size = QSize(screen_size.width() / 2, screen_size.height() / 1.75)
-
- # Create bar graph
- bars = BarGraph(minimum_graph_size, screen_size)
- # Create scatter graph
- scatter = ScatterGraph(minimum_graph_size, screen_size)
- # Create surface graph
- surface = SurfaceGraph(minimum_graph_size, screen_size)
-
- # Add bars widget
- tabWidget.addTab(bars.barsWidget(), "Bar Graph")
- # Add scatter widget
- tabWidget.addTab(scatter.scatterWidget(), "Scatter Graph")
- # Add surface widget
- tabWidget.addTab(surface.surfaceWidget(), "Surface Graph")
-
- tabWidget.show()
- sys.exit(app.exec())
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import sys
-
-from pathlib import Path
-
-from PySide6.QtCore import QFile, QIODevice, QObject
-from PySide6.QtGraphs import (QBar3DSeries, QCategory3DAxis, QValue3DAxis)
-
-from variantbardataproxy import VariantBarDataProxy
-from variantbardatamapping import VariantBarDataMapping
-from variantdataset import VariantDataSet
-
-
-MONTHS = ["January", "February", "March", "April",
- "May", "June", "July", "August", "September", "October",
- "November", "December"]
-
-
-class RainfallData(QObject):
-
- def __init__(self):
- super().__init__()
- self._columnCount = 0
- self._rowCount = 0
- self._years = []
- self._numericMonths = []
- self._proxy = VariantBarDataProxy()
- self._mapping = None
- self._dataSet = None
- self._series = QBar3DSeries()
- self._valueAxis = QValue3DAxis()
- self._rowAxis = QCategory3DAxis()
- self._colAxis = QCategory3DAxis()
-
- # In data file the months are in numeric format, so create custom list
- for i in range(1, 13):
- self._numericMonths.append(str(i))
-
- self._columnCount = len(self._numericMonths)
-
- self.updateYearsList(2010, 2022)
-
- # Create proxy and series
- self._proxy = VariantBarDataProxy()
- self._series = QBar3DSeries(self._proxy)
-
- self._series.setItemLabelFormat("%.1f mm")
-
- # Create the axes
- self._rowAxis = QCategory3DAxis(self)
- self._colAxis = QCategory3DAxis(self)
- self._valueAxis = QValue3DAxis(self)
- self._rowAxis.setAutoAdjustRange(True)
- self._colAxis.setAutoAdjustRange(True)
- self._valueAxis.setAutoAdjustRange(True)
-
- # Set axis labels and titles
- self._rowAxis.setTitle("Year")
- self._colAxis.setTitle("Month")
- self._valueAxis.setTitle("rainfall (mm)")
- self._valueAxis.setSegmentCount(5)
- self._rowAxis.setLabels(self._years)
- self._colAxis.setLabels(MONTHS)
- self._rowAxis.setTitleVisible(True)
- self._colAxis.setTitleVisible(True)
- self._valueAxis.setTitleVisible(True)
-
- self.addDataSet()
-
- def customSeries(self):
- return self._series
-
- def valueAxis(self):
- return self._valueAxis
-
- def rowAxis(self):
- return self._rowAxis
-
- def colAxis(self):
- return self._colAxis
-
- def updateYearsList(self, start, end):
- self._years.clear()
- for i in range(start, end + 1):
- self._years.append(str(i))
- self._rowCount = len(self._years)
-
- def addDataSet(self):
- # Create a new variant data set and data item list
- self._dataSet = VariantDataSet()
- itemList = []
-
- # Read data from a data file into the data item list
- file_path = Path(__file__).resolve().parent / "data" / "raindata.txt"
- dataFile = QFile(file_path)
- if dataFile.open(QIODevice.ReadOnly | QIODevice.Text):
- data = dataFile.readAll().data().decode("utf8")
- for line in data.split("\n"):
- if line and not line.startswith("#"): # Ignore comments
- tokens = line.split(",")
- # Each line has three data items: Year, month, and
- # rainfall value
- if len(tokens) >= 3:
- # Store year and month as strings, and rainfall value
- # as double into a variant data item and add the item to
- # the item list.
- newItem = []
- newItem.append(tokens[0].strip())
- newItem.append(tokens[1].strip())
- newItem.append(float(tokens[2].strip()))
- itemList.append(newItem)
- else:
- print("Unable to open data file:", dataFile.fileName(),
- file=sys.stderr)
-
- # Add items to the data set and set it to the proxy
- self._dataSet.addItems(itemList)
- self._proxy.setDataSet(self._dataSet)
-
- # Create new mapping for the data and set it to the proxy
- self._mapping = VariantBarDataMapping(0, 1, 2,
- self._years, self._numericMonths)
- self._proxy.setMapping(self._mapping)
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from math import cos, degrees, sqrt
-
-from PySide6.QtCore import QObject, Signal, Slot, Qt
-from PySide6.QtGui import QVector3D
-from PySide6.QtGraphs import (QAbstract3DGraph, QAbstract3DSeries,
- QScatterDataItem, QScatterDataProxy,
- QScatter3DSeries, Q3DCamera, Q3DTheme)
-
-from axesinputhandler import AxesInputHandler
-
-
-NUMBER_OF_ITEMS = 10000
-CURVE_DIVIDER = 7.5
-LOWER_NUMBER_OF_ITEMS = 900
-LOWER_CURVE_DIVIDER = 0.75
-
-
-class ScatterDataModifier(QObject):
-
- backgroundEnabledChanged = Signal(bool)
- gridEnabledChanged = Signal(bool)
- shadowQualityChanged = Signal(int)
-
- def __init__(self, scatter, parent):
- super().__init__(parent)
-
- self._graph = scatter
-
- self._style = QAbstract3DSeries.MeshSphere
- self._smooth = True
- self._inputHandler = AxesInputHandler(scatter)
- self._autoAdjust = True
- self._itemCount = LOWER_NUMBER_OF_ITEMS
- self._CURVE_DIVIDER = LOWER_CURVE_DIVIDER
- self._inputHandler = AxesInputHandler(scatter)
-
- self._graph.activeTheme().setType(Q3DTheme.ThemeStoneMoss)
- self._graph.setShadowQuality(QAbstract3DGraph.ShadowQualitySoftHigh)
- self._graph.scene().activeCamera().setCameraPreset(Q3DCamera.CameraPresetFront)
- self._graph.scene().activeCamera().setZoomLevel(80.0)
-
- self._proxy = QScatterDataProxy()
- self._series = QScatter3DSeries(self._proxy)
- self._series.setItemLabelFormat("@xTitle: @xLabel @yTitle: @yLabel @zTitle: @zLabel")
- self._series.setMeshSmooth(self._smooth)
- self._graph.addSeries(self._series)
-
- # Give ownership of the handler to the graph and make it the active
- # handler
- self._graph.setActiveInputHandler(self._inputHandler)
-
- # Give our axes to the input handler
- self._inputHandler.setAxes(self._graph.axisX(), self._graph.axisZ(),
- self._graph.axisY())
-
- self.addData()
-
- def addData(self):
- # Configure the axes according to the data
- self._graph.axisX().setTitle("X")
- self._graph.axisY().setTitle("Y")
- self._graph.axisZ().setTitle("Z")
-
- dataArray = []
- limit = int(sqrt(self._itemCount) / 2.0)
- for i in range(-limit, limit):
- for j in range(-limit, limit):
- x = float(i) + 0.5
- y = cos(degrees(float(i * j) / self._CURVE_DIVIDER))
- z = float(j) + 0.5
- dataArray.append(QScatterDataItem(QVector3D(x, y, z)))
-
- self._graph.seriesList()[0].dataProxy().resetArray(dataArray)
-
- @Slot(int)
- def changeStyle(self, style):
- comboBox = self.sender()
- if comboBox:
- self._style = comboBox.itemData(style)
- if self._graph.seriesList():
- self._graph.seriesList()[0].setMesh(self._style)
-
- @Slot(int)
- def setSmoothDots(self, smooth):
- self._smooth = smooth == Qt.Checked.value
- series = self._graph.seriesList()[0]
- series.setMeshSmooth(self._smooth)
-
- @Slot(int)
- def changeTheme(self, theme):
- currentTheme = self._graph.activeTheme()
- currentTheme.setType(Q3DTheme.Theme(theme))
- self.backgroundEnabledChanged.emit(currentTheme.isBackgroundEnabled())
- self.gridEnabledChanged.emit(currentTheme.isGridEnabled())
-
- @Slot()
- def changePresetCamera(self):
- preset = Q3DCamera.CameraPresetFrontLow.value
-
- camera = self._graph.scene().activeCamera()
- camera.setCameraPreset(Q3DCamera.CameraPreset(preset))
-
- preset += 1
- if preset > Q3DCamera.CameraPresetDirectlyBelow.value:
- preset = Q3DCamera.CameraPresetFrontLow.value
-
- @Slot(QAbstract3DGraph.ShadowQuality)
- def shadowQualityUpdatedByVisual(self, sq):
- self.shadowQualityChanged.emit(sq.value)
-
- @Slot(int)
- def changeShadowQuality(self, quality):
- sq = QAbstract3DGraph.ShadowQuality(quality)
- self._graph.setShadowQuality(sq)
-
- @Slot(int)
- def setBackgroundEnabled(self, enabled):
- self._graph.activeTheme().setBackgroundEnabled(enabled == Qt.Checked.value)
-
- @Slot(int)
- def setGridEnabled(self, enabled):
- self._graph.activeTheme().setGridEnabled(enabled == Qt.Checked.value)
-
- @Slot()
- def toggleItemCount(self):
- if self._itemCount == NUMBER_OF_ITEMS:
- self._itemCount = LOWER_NUMBER_OF_ITEMS
- self._CURVE_DIVIDER = LOWER_CURVE_DIVIDER
- else:
- self._itemCount = NUMBER_OF_ITEMS
- self._CURVE_DIVIDER = CURVE_DIVIDER
-
- self._graph.seriesList()[0].dataProxy().resetArray([])
- self.addData()
-
- @Slot()
- def toggleRanges(self):
- if not self._autoAdjust:
- self._graph.axisX().setAutoAdjustRange(True)
- self._graph.axisZ().setAutoAdjustRange(True)
- self._inputHandler.setDragSpeedModifier(1.5)
- self._autoAdjust = True
- else:
- self._graph.axisX().setRange(-10.0, 10.0)
- self._graph.axisZ().setRange(-10.0, 10.0)
- self._inputHandler.setDragSpeedModifier(15.0)
- self._autoAdjust = False
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from PySide6.QtCore import QObject, QSize, Qt
-from PySide6.QtWidgets import (QCheckBox, QComboBox, QCommandLinkButton,
- QLabel, QHBoxLayout, QSizePolicy,
- QVBoxLayout, QWidget, )
-from PySide6.QtQuickWidgets import QQuickWidget
-from PySide6.QtGraphs import (QAbstract3DSeries, Q3DScatter)
-
-from scatterdatamodifier import ScatterDataModifier
-
-
-class ScatterGraph(QObject):
-
- def __init__(self, minimum_graph_size, maximum_graph_size):
- super().__init__()
- self._scatterGraph = Q3DScatter()
- self._scatterWidget = QWidget()
- hLayout = QHBoxLayout(self._scatterWidget)
- self._scatterGraph.setMinimumSize(minimum_graph_size)
- self._scatterGraph.setMaximumSize(maximum_graph_size)
- self._scatterGraph.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
- self._scatterGraph.setFocusPolicy(Qt.StrongFocus)
- self._scatterGraph.setResizeMode(QQuickWidget.SizeRootObjectToView)
- hLayout.addWidget(self._scatterGraph, 1)
-
- vLayout = QVBoxLayout()
- hLayout.addLayout(vLayout)
-
- cameraButton = QCommandLinkButton(self._scatterWidget)
- cameraButton.setText("Change camera preset")
- cameraButton.setDescription("Switch between a number of preset camera positions")
- cameraButton.setIconSize(QSize(0, 0))
-
- itemCountButton = QCommandLinkButton(self._scatterWidget)
- itemCountButton.setText("Toggle item count")
- itemCountButton.setDescription("Switch between 900 and 10000 data points")
- itemCountButton.setIconSize(QSize(0, 0))
-
- rangeButton = QCommandLinkButton(self._scatterWidget)
- rangeButton.setText("Toggle axis ranges")
- rangeButton.setDescription("Switch between automatic axis ranges and preset ranges")
- rangeButton.setIconSize(QSize(0, 0))
-
- backgroundCheckBox = QCheckBox(self._scatterWidget)
- backgroundCheckBox.setText("Show background")
- backgroundCheckBox.setChecked(True)
-
- gridCheckBox = QCheckBox(self._scatterWidget)
- gridCheckBox.setText("Show grid")
- gridCheckBox.setChecked(True)
-
- smoothCheckBox = QCheckBox(self._scatterWidget)
- smoothCheckBox.setText("Smooth dots")
- smoothCheckBox.setChecked(True)
-
- itemStyleList = QComboBox(self._scatterWidget)
- itemStyleList.addItem("Sphere", QAbstract3DSeries.MeshSphere)
- itemStyleList.addItem("Cube", QAbstract3DSeries.MeshCube)
- itemStyleList.addItem("Minimal", QAbstract3DSeries.MeshMinimal)
- itemStyleList.addItem("Point", QAbstract3DSeries.MeshPoint)
- itemStyleList.setCurrentIndex(0)
-
- themeList = QComboBox(self._scatterWidget)
- themeList.addItem("Qt")
- themeList.addItem("Primary Colors")
- themeList.addItem("Digia")
- themeList.addItem("Stone Moss")
- themeList.addItem("Army Blue")
- themeList.addItem("Retro")
- themeList.addItem("Ebony")
- themeList.addItem("Isabelle")
- themeList.setCurrentIndex(3)
-
- shadowQuality = QComboBox(self._scatterWidget)
- shadowQuality.addItem("None")
- shadowQuality.addItem("Low")
- shadowQuality.addItem("Medium")
- shadowQuality.addItem("High")
- shadowQuality.addItem("Low Soft")
- shadowQuality.addItem("Medium Soft")
- shadowQuality.addItem("High Soft")
- shadowQuality.setCurrentIndex(6)
-
- vLayout.addWidget(cameraButton)
- vLayout.addWidget(itemCountButton)
- vLayout.addWidget(rangeButton)
- vLayout.addWidget(backgroundCheckBox)
- vLayout.addWidget(gridCheckBox)
- vLayout.addWidget(smoothCheckBox)
- vLayout.addWidget(QLabel("Change dot style"))
- vLayout.addWidget(itemStyleList)
- vLayout.addWidget(QLabel("Change theme"))
- vLayout.addWidget(themeList)
- vLayout.addWidget(QLabel("Adjust shadow quality"))
- vLayout.addWidget(shadowQuality, 1, Qt.AlignTop)
-
- self._modifier = ScatterDataModifier(self._scatterGraph, self)
-
- cameraButton.clicked.connect(self._modifier.changePresetCamera)
- itemCountButton.clicked.connect(self._modifier.toggleItemCount)
- rangeButton.clicked.connect(self._modifier.toggleRanges)
-
- backgroundCheckBox.stateChanged.connect(self._modifier.setBackgroundEnabled)
- gridCheckBox.stateChanged.connect(self._modifier.setGridEnabled)
- smoothCheckBox.stateChanged.connect(self._modifier.setSmoothDots)
-
- self._modifier.backgroundEnabledChanged.connect(backgroundCheckBox.setChecked)
- self._modifier.gridEnabledChanged.connect(gridCheckBox.setChecked)
- itemStyleList.currentIndexChanged.connect(self._modifier.changeStyle)
-
- themeList.currentIndexChanged.connect(self._modifier.changeTheme)
-
- shadowQuality.currentIndexChanged.connect(self._modifier.changeShadowQuality)
-
- self._modifier.shadowQualityChanged.connect(shadowQuality.setCurrentIndex)
- self._scatterGraph.shadowQualityChanged.connect(self._modifier.shadowQualityUpdatedByVisual)
-
- def scatterWidget(self):
- return self._scatterWidget
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from surfacegraphmodifier import SurfaceGraphModifier
-
-from PySide6.QtCore import QObject, Qt
-from PySide6.QtGui import QBrush, QIcon, QLinearGradient, QPainter, QPixmap
-from PySide6.QtWidgets import (QGroupBox, QCheckBox, QLabel, QHBoxLayout,
- QPushButton, QRadioButton, QSizePolicy, QSlider,
- QVBoxLayout, QWidget)
-from PySide6.QtQuickWidgets import QQuickWidget
-from PySide6.QtGraphs import Q3DSurface
-
-
-def gradientBtoYPB_Pixmap():
- grBtoY = QLinearGradient(0, 0, 1, 100)
- grBtoY.setColorAt(1.0, Qt.black)
- grBtoY.setColorAt(0.67, Qt.blue)
- grBtoY.setColorAt(0.33, Qt.red)
- grBtoY.setColorAt(0.0, Qt.yellow)
- pm = QPixmap(24, 100)
- with QPainter(pm) as pmp:
- pmp.setBrush(QBrush(grBtoY))
- pmp.setPen(Qt.NoPen)
- pmp.drawRect(0, 0, 24, 100)
- return pm
-
-
-def gradientGtoRPB_Pixmap():
- grGtoR = QLinearGradient(0, 0, 1, 100)
- grGtoR.setColorAt(1.0, Qt.darkGreen)
- grGtoR.setColorAt(0.5, Qt.yellow)
- grGtoR.setColorAt(0.2, Qt.red)
- grGtoR.setColorAt(0.0, Qt.darkRed)
- pm = QPixmap(24, 100)
- with QPainter(pm) as pmp:
- pmp.setBrush(QBrush(grGtoR))
- pmp.setPen(Qt.NoPen)
- pmp.drawRect(0, 0, 24, 100)
- return pm
-
-
-def highlightPixmap():
- HEIGHT = 400
- WIDTH = 110
- BORDER = 10
- gr = QLinearGradient(0, 0, 1, HEIGHT - 2 * BORDER)
- gr.setColorAt(1.0, Qt.black)
- gr.setColorAt(0.8, Qt.darkGreen)
- gr.setColorAt(0.6, Qt.green)
- gr.setColorAt(0.4, Qt.yellow)
- gr.setColorAt(0.2, Qt.red)
- gr.setColorAt(0.0, Qt.darkRed)
- pmHighlight = QPixmap(WIDTH, HEIGHT)
- pmHighlight.fill(Qt.transparent)
- with QPainter(pmHighlight) as pmpHighlight:
- pmpHighlight.setBrush(QBrush(gr))
- pmpHighlight.setPen(Qt.NoPen)
- pmpHighlight.drawRect(BORDER, BORDER, 35, HEIGHT - 2 * BORDER)
- pmpHighlight.setPen(Qt.black)
- step = (HEIGHT - 2 * BORDER) / 5
- for i in range(0, 6):
- yPos = i * step + BORDER
- pmpHighlight.drawLine(BORDER, yPos, 55, yPos)
- HEIGHT = 550 - (i * 110)
- pmpHighlight.drawText(60, yPos + 2, f"{HEIGHT} m")
- return pmHighlight
-
-
-class SurfaceGraph(QObject):
-
- def __init__(self, minimum_graph_size, maximum_graph_size):
- super().__init__()
- self._surfaceGraph = Q3DSurface()
- self._surfaceWidget = QWidget()
- hLayout = QHBoxLayout(self._surfaceWidget)
- self._surfaceGraph.setMinimumSize(minimum_graph_size)
- self._surfaceGraph.setMaximumSize(maximum_graph_size)
- self._surfaceGraph.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
- self._surfaceGraph.setFocusPolicy(Qt.StrongFocus)
- self._surfaceGraph.setResizeMode(QQuickWidget.SizeRootObjectToView)
- hLayout.addWidget(self._surfaceGraph, 1)
- vLayout = QVBoxLayout()
- hLayout.addLayout(vLayout)
- vLayout.setAlignment(Qt.AlignTop)
- # Create control widgets
- modelGroupBox = QGroupBox("Model")
- sqrtSinModelRB = QRadioButton(self._surfaceWidget)
- sqrtSinModelRB.setText("Sqrt and Sin")
- sqrtSinModelRB.setChecked(False)
- heightMapModelRB = QRadioButton(self._surfaceWidget)
- heightMapModelRB.setText("Multiseries\nHeight Map")
- heightMapModelRB.setChecked(False)
- texturedModelRB = QRadioButton(self._surfaceWidget)
- texturedModelRB.setText("Textured\nTopography")
- texturedModelRB.setChecked(False)
- modelVBox = QVBoxLayout()
- modelVBox.addWidget(sqrtSinModelRB)
- modelVBox.addWidget(heightMapModelRB)
- modelVBox.addWidget(texturedModelRB)
- modelGroupBox.setLayout(modelVBox)
- selectionGroupBox = QGroupBox("Graph Selection Mode")
- modeNoneRB = QRadioButton(self._surfaceWidget)
- modeNoneRB.setText("No selection")
- modeNoneRB.setChecked(False)
- modeItemRB = QRadioButton(self._surfaceWidget)
- modeItemRB.setText("Item")
- modeItemRB.setChecked(False)
- modeSliceRowRB = QRadioButton(self._surfaceWidget)
- modeSliceRowRB.setText("Row Slice")
- modeSliceRowRB.setChecked(False)
- modeSliceColumnRB = QRadioButton(self._surfaceWidget)
- modeSliceColumnRB.setText("Column Slice")
- modeSliceColumnRB.setChecked(False)
- selectionVBox = QVBoxLayout()
- selectionVBox.addWidget(modeNoneRB)
- selectionVBox.addWidget(modeItemRB)
- selectionVBox.addWidget(modeSliceRowRB)
- selectionVBox.addWidget(modeSliceColumnRB)
- selectionGroupBox.setLayout(selectionVBox)
- axisGroupBox = QGroupBox("Axis ranges")
- axisMinSliderX = QSlider(Qt.Horizontal)
- axisMinSliderX.setMinimum(0)
- axisMinSliderX.setTickInterval(1)
- axisMinSliderX.setEnabled(True)
- axisMaxSliderX = QSlider(Qt.Horizontal)
- axisMaxSliderX.setMinimum(1)
- axisMaxSliderX.setTickInterval(1)
- axisMaxSliderX.setEnabled(True)
- axisMinSliderZ = QSlider(Qt.Horizontal)
- axisMinSliderZ.setMinimum(0)
- axisMinSliderZ.setTickInterval(1)
- axisMinSliderZ.setEnabled(True)
- axisMaxSliderZ = QSlider(Qt.Horizontal)
- axisMaxSliderZ.setMinimum(1)
- axisMaxSliderZ.setTickInterval(1)
- axisMaxSliderZ.setEnabled(True)
- axisVBox = QVBoxLayout(axisGroupBox)
- axisVBox.addWidget(QLabel("Column range"))
- axisVBox.addWidget(axisMinSliderX)
- axisVBox.addWidget(axisMaxSliderX)
- axisVBox.addWidget(QLabel("Row range"))
- axisVBox.addWidget(axisMinSliderZ)
- axisVBox.addWidget(axisMaxSliderZ)
- # Mode-dependent controls
- # sqrt-sin
- colorGroupBox = QGroupBox("Custom gradient")
-
- pixmap = gradientBtoYPB_Pixmap()
- gradientBtoYPB = QPushButton(self._surfaceWidget)
- gradientBtoYPB.setIcon(QIcon(pixmap))
- gradientBtoYPB.setIconSize(pixmap.size())
-
- pixmap = gradientGtoRPB_Pixmap()
- gradientGtoRPB = QPushButton(self._surfaceWidget)
- gradientGtoRPB.setIcon(QIcon(pixmap))
- gradientGtoRPB.setIconSize(pixmap.size())
-
- colorHBox = QHBoxLayout(colorGroupBox)
- colorHBox.addWidget(gradientBtoYPB)
- colorHBox.addWidget(gradientGtoRPB)
- # Multiseries heightmap
- showGroupBox = QGroupBox("Show Object")
- showGroupBox.setVisible(False)
- checkboxShowOilRigOne = QCheckBox("Oil Rig 1")
- checkboxShowOilRigOne.setChecked(True)
- checkboxShowOilRigTwo = QCheckBox("Oil Rig 2")
- checkboxShowOilRigTwo.setChecked(True)
- checkboxShowRefinery = QCheckBox("Refinery")
- showVBox = QVBoxLayout()
- showVBox.addWidget(checkboxShowOilRigOne)
- showVBox.addWidget(checkboxShowOilRigTwo)
- showVBox.addWidget(checkboxShowRefinery)
- showGroupBox.setLayout(showVBox)
- visualsGroupBox = QGroupBox("Visuals")
- visualsGroupBox.setVisible(False)
- checkboxVisualsSeeThrough = QCheckBox("See-Through")
- checkboxHighlightOil = QCheckBox("Highlight Oil")
- checkboxShowShadows = QCheckBox("Shadows")
- checkboxShowShadows.setChecked(True)
- visualVBox = QVBoxLayout(visualsGroupBox)
- visualVBox.addWidget(checkboxVisualsSeeThrough)
- visualVBox.addWidget(checkboxHighlightOil)
- visualVBox.addWidget(checkboxShowShadows)
- labelSelection = QLabel("Selection:")
- labelSelection.setVisible(False)
- labelSelectedItem = QLabel("Nothing")
- labelSelectedItem.setVisible(False)
- # Textured topography heightmap
- enableTexture = QCheckBox("Surface texture")
- enableTexture.setVisible(False)
-
- label = QLabel(self._surfaceWidget)
- label.setPixmap(highlightPixmap())
- heightMapGroupBox = QGroupBox("Highlight color map")
- colorMapVBox = QVBoxLayout()
- colorMapVBox.addWidget(label)
- heightMapGroupBox.setLayout(colorMapVBox)
- heightMapGroupBox.setVisible(False)
- # Populate vertical layout
- # Common
- vLayout.addWidget(modelGroupBox)
- vLayout.addWidget(selectionGroupBox)
- vLayout.addWidget(axisGroupBox)
- # Sqrt Sin
- vLayout.addWidget(colorGroupBox)
- # Multiseries heightmap
- vLayout.addWidget(showGroupBox)
- vLayout.addWidget(visualsGroupBox)
- vLayout.addWidget(labelSelection)
- vLayout.addWidget(labelSelectedItem)
- # Textured topography
- vLayout.addWidget(heightMapGroupBox)
- vLayout.addWidget(enableTexture)
- # Create the controller
- modifier = SurfaceGraphModifier(self._surfaceGraph, labelSelectedItem, self)
- # Connect widget controls to controller
- heightMapModelRB.toggled.connect(modifier.enableHeightMapModel)
- sqrtSinModelRB.toggled.connect(modifier.enableSqrtSinModel)
- texturedModelRB.toggled.connect(modifier.enableTopographyModel)
- modeNoneRB.toggled.connect(modifier.toggleModeNone)
- modeItemRB.toggled.connect(modifier.toggleModeItem)
- modeSliceRowRB.toggled.connect(modifier.toggleModeSliceRow)
- modeSliceColumnRB.toggled.connect(modifier.toggleModeSliceColumn)
- axisMinSliderX.valueChanged.connect(modifier.adjustXMin)
- axisMaxSliderX.valueChanged.connect(modifier.adjustXMax)
- axisMinSliderZ.valueChanged.connect(modifier.adjustZMin)
- axisMaxSliderZ.valueChanged.connect(modifier.adjustZMax)
- # Mode dependent connections
- gradientBtoYPB.pressed.connect(modifier.setBlackToYellowGradient)
- gradientGtoRPB.pressed.connect(modifier.setGreenToRedGradient)
- checkboxShowOilRigOne.stateChanged.connect(modifier.toggleItemOne)
- checkboxShowOilRigTwo.stateChanged.connect(modifier.toggleItemTwo)
- checkboxShowRefinery.stateChanged.connect(modifier.toggleItemThree)
- checkboxVisualsSeeThrough.stateChanged.connect(modifier.toggleSeeThrough)
- checkboxHighlightOil.stateChanged.connect(modifier.toggleOilHighlight)
- checkboxShowShadows.stateChanged.connect(modifier.toggleShadows)
- enableTexture.stateChanged.connect(modifier.toggleSurfaceTexture)
- # Connections to disable features depending on mode
- sqrtSinModelRB.toggled.connect(colorGroupBox.setVisible)
- heightMapModelRB.toggled.connect(showGroupBox.setVisible)
- heightMapModelRB.toggled.connect(visualsGroupBox.setVisible)
- heightMapModelRB.toggled.connect(labelSelection.setVisible)
- heightMapModelRB.toggled.connect(labelSelectedItem.setVisible)
- texturedModelRB.toggled.connect(enableTexture.setVisible)
- texturedModelRB.toggled.connect(heightMapGroupBox.setVisible)
- modifier.setAxisMinSliderX(axisMinSliderX)
- modifier.setAxisMaxSliderX(axisMaxSliderX)
- modifier.setAxisMinSliderZ(axisMinSliderZ)
- modifier.setAxisMaxSliderZ(axisMaxSliderZ)
- sqrtSinModelRB.setChecked(True)
- modeItemRB.setChecked(True)
- enableTexture.setChecked(True)
-
- def surfaceWidget(self):
- return self._surfaceWidget
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import os
-from math import sqrt, sin
-from pathlib import Path
-
-from PySide6.QtCore import QObject, QPropertyAnimation, Qt, Slot
-from PySide6.QtGui import (QColor, QFont, QImage, QLinearGradient,
- QQuaternion, QVector3D)
-from PySide6.QtGraphs import (QAbstract3DGraph, QCustom3DItem,
- QCustom3DLabel, QHeightMapSurfaceDataProxy,
- QValue3DAxis, QSurfaceDataItem,
- QSurfaceDataProxy, QSurface3DSeries,
- Q3DInputHandler, Q3DCamera, Q3DTheme)
-
-
-from highlightseries import HighlightSeries
-from topographicseries import TopographicSeries
-from custominputhandler import CustomInputHandler
-
-
-SAMPLE_COUNT_X = 150
-SAMPLE_COUNT_Z = 150
-HEIGHTMAP_GRID_STEP_X = 6
-HEIGHTMAP_GRID_STEP_Z = 6
-SAMPLE_MIN = -8.0
-SAMPLE_MAX = 8.0
-
-AREA_WIDTH = 8000.0
-AREA_HEIGHT = 8000.0
-ASPECT_RATIO = 0.1389
-MIN_RANGE = AREA_WIDTH * 0.49
-
-
-class SurfaceGraphModifier(QObject):
-
- def __init__(self, surface, label, parent):
- super().__init__(parent)
- self._data_path = Path(__file__).resolve().parent / "data"
- self._graph = surface
- self._textField = label
- self._sqrtSinProxy = None
- self._sqrtSinSeries = None
- self._heightMapProxyOne = None
- self._heightMapProxyTwo = None
- self._heightMapProxyThree = None
- self._heightMapSeriesOne = None
- self._heightMapSeriesTwo = None
- self._heightMapSeriesThree = None
-
- self._axisMinSliderX = None
- self._axisMaxSliderX = None
- self._axisMinSliderZ = None
- self._axisMaxSliderZ = None
- self._rangeMinX = 0.0
- self._rangeMinZ = 0.0
- self._stepX = 0.0
- self._stepZ = 0.0
- self._heightMapWidth = 0
- self._heightMapHeight = 0
-
- self._selectionAnimation = None
- self._titleLabel = None
- self._previouslyAnimatedItem = None
- self._previousScaling = {}
-
- self._topography = None
- self._highlight = None
- self._highlightWidth = 0
- self._highlightHeight = 0
-
- self._customInputHandler = None
- self._defaultInputHandler = Q3DInputHandler()
-
- ac = self._graph.scene().activeCamera()
- ac.setZoomLevel(85.0)
- ac.setCameraPreset(Q3DCamera.CameraPresetIsometricRight)
- self._graph.activeTheme().setType(Q3DTheme.ThemeRetro)
-
- self._x_axis = QValue3DAxis()
- self._y_axis = QValue3DAxis()
- self._z_axis = QValue3DAxis()
- self._graph.setAxisX(self._x_axis)
- self._graph.setAxisY(self._y_axis)
- self._graph.setAxisZ(self._z_axis)
-
- #
- # Sqrt Sin
- #
- self._sqrtSinProxy = QSurfaceDataProxy()
- self._sqrtSinSeries = QSurface3DSeries(self._sqrtSinProxy)
- self.fillSqrtSinProxy()
-
- #
- # Multisurface heightmap
- #
- # Create the first surface layer
- heightMapImageOne = QImage(self._data_path / "layer_1.png")
- self._heightMapProxyOne = QHeightMapSurfaceDataProxy(heightMapImageOne)
- self._heightMapSeriesOne = QSurface3DSeries(self._heightMapProxyOne)
- self._heightMapSeriesOne.setItemLabelFormat("(@xLabel, @zLabel): @yLabel")
- self._heightMapProxyOne.setValueRanges(34.0, 40.0, 18.0, 24.0)
-
- # Create the other 2 surface layers
- heightMapImageTwo = QImage(self._data_path / "layer_2.png")
- self._heightMapProxyTwo = QHeightMapSurfaceDataProxy(heightMapImageTwo)
- self._heightMapSeriesTwo = QSurface3DSeries(self._heightMapProxyTwo)
- self._heightMapSeriesTwo.setItemLabelFormat("(@xLabel, @zLabel): @yLabel")
- self._heightMapProxyTwo.setValueRanges(34.0, 40.0, 18.0, 24.0)
-
- heightMapImageThree = QImage(self._data_path / "layer_3.png")
- self._heightMapProxyThree = QHeightMapSurfaceDataProxy(heightMapImageThree)
- self._heightMapSeriesThree = QSurface3DSeries(self._heightMapProxyThree)
- self._heightMapSeriesThree.setItemLabelFormat("(@xLabel, @zLabel): @yLabel")
- self._heightMapProxyThree.setValueRanges(34.0, 40.0, 18.0, 24.0)
-
- # The images are the same size, so it's enough to get the dimensions
- # from one
- self._heightMapWidth = heightMapImageOne.width()
- self._heightMapHeight = heightMapImageOne.height()
-
- # Set the gradients for multi-surface layers
- grOne = QLinearGradient()
- grOne.setColorAt(0.0, Qt.black)
- grOne.setColorAt(0.38, Qt.darkYellow)
- grOne.setColorAt(0.39, Qt.darkGreen)
- grOne.setColorAt(0.5, Qt.darkGray)
- grOne.setColorAt(1.0, Qt.gray)
- self._heightMapSeriesOne.setBaseGradient(grOne)
- self._heightMapSeriesOne.setColorStyle(Q3DTheme.ColorStyleRangeGradient)
-
- grTwo = QLinearGradient()
- grTwo.setColorAt(0.39, Qt.blue)
- grTwo.setColorAt(0.4, Qt.white)
- self._heightMapSeriesTwo.setBaseGradient(grTwo)
- self._heightMapSeriesTwo.setColorStyle(Q3DTheme.ColorStyleRangeGradient)
-
- grThree = QLinearGradient()
- grThree.setColorAt(0.0, Qt.white)
- grThree.setColorAt(0.05, Qt.black)
- self._heightMapSeriesThree.setBaseGradient(grThree)
- self._heightMapSeriesThree.setColorStyle(Q3DTheme.ColorStyleRangeGradient)
-
- # Custom items and label
- self._graph.selectedElementChanged.connect(self.handleElementSelected)
-
- self._selectionAnimation = QPropertyAnimation(self)
- self._selectionAnimation.setPropertyName(b"scaling")
- self._selectionAnimation.setDuration(500)
- self._selectionAnimation.setLoopCount(-1)
-
- titleFont = QFont("Century Gothic", 30)
- titleFont.setBold(True)
- self._titleLabel = QCustom3DLabel("Oil Rigs on Imaginary Sea", titleFont,
- QVector3D(0.0, 1.2, 0.0),
- QVector3D(1.0, 1.0, 0.0),
- QQuaternion())
- self._titleLabel.setPositionAbsolute(True)
- self._titleLabel.setFacingCamera(True)
- self._titleLabel.setBackgroundColor(QColor(0x66cdaa))
- self._graph.addCustomItem(self._titleLabel)
- self._titleLabel.setVisible(False)
-
- # Make two of the custom object visible
- self.toggleItemOne(True)
- self.toggleItemTwo(True)
-
- #
- # Topographic map
- #
- self._topography = TopographicSeries()
- file_name = os.fspath(self._data_path / "topography.png")
- self._topography.setTopographyFile(file_name, AREA_WIDTH, AREA_HEIGHT)
- self._topography.setItemLabelFormat("@yLabel m")
-
- self._highlight = HighlightSeries()
- self._highlight.setTopographicSeries(self._topography)
- self._highlight.setMinHeight(MIN_RANGE * ASPECT_RATIO)
- self._highlight.handleGradientChange(AREA_WIDTH * ASPECT_RATIO)
- self._graph.axisY().maxChanged.connect(self._highlight.handleGradientChange)
-
- self._customInputHandler = CustomInputHandler(self._graph)
- self._customInputHandler.setHighlightSeries(self._highlight)
- self._customInputHandler.setAxes(self._x_axis, self._y_axis, self._z_axis)
- self._customInputHandler.setLimits(0.0, AREA_WIDTH, MIN_RANGE)
- self._customInputHandler.setAspectRatio(ASPECT_RATIO)
-
- def fillSqrtSinProxy(self):
- stepX = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_X - 1)
- stepZ = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_Z - 1)
-
- dataArray = []
- for i in range(0, SAMPLE_COUNT_Z):
- newRow = []
- # Keep values within range bounds, since just adding step can
- # cause minor drift due to the rounding errors.
- z = min(SAMPLE_MAX, (i * stepZ + SAMPLE_MIN))
- for j in range(0, SAMPLE_COUNT_X):
- x = min(SAMPLE_MAX, (j * stepX + SAMPLE_MIN))
- R = sqrt(z * z + x * x) + 0.01
- y = (sin(R) / R + 0.24) * 1.61
- item = QSurfaceDataItem(QVector3D(x, y, z))
- newRow.append(item)
- dataArray.append(newRow)
- self._sqrtSinProxy.resetArray(dataArray)
-
- @Slot(bool)
- def enableSqrtSinModel(self, enable):
- if enable:
- self._sqrtSinSeries.setDrawMode(QSurface3DSeries.DrawSurfaceAndWireframe)
- self._sqrtSinSeries.setFlatShadingEnabled(True)
-
- self._graph.axisX().setLabelFormat("%.2f")
- self._graph.axisZ().setLabelFormat("%.2f")
- self._graph.axisX().setRange(SAMPLE_MIN, SAMPLE_MAX)
- self._graph.axisY().setRange(0.0, 2.0)
- self._graph.axisZ().setRange(SAMPLE_MIN, SAMPLE_MAX)
- self._graph.axisX().setLabelAutoRotation(30.0)
- self._graph.axisY().setLabelAutoRotation(90.0)
- self._graph.axisZ().setLabelAutoRotation(30.0)
-
- self._graph.removeSeries(self._heightMapSeriesOne)
- self._graph.removeSeries(self._heightMapSeriesTwo)
- self._graph.removeSeries(self._heightMapSeriesThree)
- self._graph.removeSeries(self._topography)
- self._graph.removeSeries(self._highlight)
-
- self._graph.addSeries(self._sqrtSinSeries)
-
- self._titleLabel.setVisible(False)
- self._graph.axisX().setTitleVisible(False)
- self._graph.axisY().setTitleVisible(False)
- self._graph.axisZ().setTitleVisible(False)
-
- self._graph.axisX().setTitle("")
- self._graph.axisY().setTitle("")
- self._graph.axisZ().setTitle("")
-
- self._graph.setActiveInputHandler(self._defaultInputHandler)
-
- # Reset range sliders for Sqrt & Sin
- self._rangeMinX = SAMPLE_MIN
- self._rangeMinZ = SAMPLE_MIN
- self._stepX = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_X - 1)
- self._stepZ = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_Z - 1)
- self._axisMinSliderX.setMinimum(0)
- self._axisMinSliderX.setMaximum(SAMPLE_COUNT_X - 2)
- self._axisMinSliderX.setValue(0)
- self._axisMaxSliderX.setMinimum(1)
- self._axisMaxSliderX.setMaximum(SAMPLE_COUNT_X - 1)
- self._axisMaxSliderX.setValue(SAMPLE_COUNT_X - 1)
- self._axisMinSliderZ.setMinimum(0)
- self._axisMinSliderZ.setMaximum(SAMPLE_COUNT_Z - 2)
- self._axisMinSliderZ.setValue(0)
- self._axisMaxSliderZ.setMinimum(1)
- self._axisMaxSliderZ.setMaximum(SAMPLE_COUNT_Z - 1)
- self._axisMaxSliderZ.setValue(SAMPLE_COUNT_Z - 1)
-
- @Slot(bool)
- def enableHeightMapModel(self, enable):
- if enable:
- self._heightMapSeriesOne.setDrawMode(QSurface3DSeries.DrawSurface)
- self._heightMapSeriesOne.setFlatShadingEnabled(False)
- self._heightMapSeriesTwo.setDrawMode(QSurface3DSeries.DrawSurface)
- self._heightMapSeriesTwo.setFlatShadingEnabled(False)
- self._heightMapSeriesThree.setDrawMode(QSurface3DSeries.DrawSurface)
- self._heightMapSeriesThree.setFlatShadingEnabled(False)
-
- self._graph.axisX().setLabelFormat("%.1f N")
- self._graph.axisZ().setLabelFormat("%.1f E")
- self._graph.axisX().setRange(34.0, 40.0)
- self._graph.axisY().setAutoAdjustRange(True)
- self._graph.axisZ().setRange(18.0, 24.0)
-
- self._graph.axisX().setTitle("Latitude")
- self._graph.axisY().setTitle("Height")
- self._graph.axisZ().setTitle("Longitude")
-
- self._graph.removeSeries(self._sqrtSinSeries)
- self._graph.removeSeries(self._topography)
- self._graph.removeSeries(self._highlight)
- self._graph.addSeries(self._heightMapSeriesOne)
- self._graph.addSeries(self._heightMapSeriesTwo)
- self._graph.addSeries(self._heightMapSeriesThree)
-
- self._graph.setActiveInputHandler(self._defaultInputHandler)
-
- self._titleLabel.setVisible(True)
- self._graph.axisX().setTitleVisible(True)
- self._graph.axisY().setTitleVisible(True)
- self._graph.axisZ().setTitleVisible(True)
-
- # Reset range sliders for height map
- mapGridCountX = self._heightMapWidth / HEIGHTMAP_GRID_STEP_X
- mapGridCountZ = self._heightMapHeight / HEIGHTMAP_GRID_STEP_Z
- self._rangeMinX = 34.0
- self._rangeMinZ = 18.0
- self._stepX = 6.0 / float(mapGridCountX - 1)
- self._stepZ = 6.0 / float(mapGridCountZ - 1)
- self._axisMinSliderX.setMinimum(0)
- self._axisMinSliderX.setMaximum(mapGridCountX - 2)
- self._axisMinSliderX.setValue(0)
- self._axisMaxSliderX.setMinimum(1)
- self._axisMaxSliderX.setMaximum(mapGridCountX - 1)
- self._axisMaxSliderX.setValue(mapGridCountX - 1)
- self._axisMinSliderZ.setMinimum(0)
- self._axisMinSliderZ.setMaximum(mapGridCountZ - 2)
- self._axisMinSliderZ.setValue(0)
- self._axisMaxSliderZ.setMinimum(1)
- self._axisMaxSliderZ.setMaximum(mapGridCountZ - 1)
- self._axisMaxSliderZ.setValue(mapGridCountZ - 1)
-
- @Slot(bool)
- def enableTopographyModel(self, enable):
- if enable:
- self._graph.axisX().setLabelFormat("%i")
- self._graph.axisZ().setLabelFormat("%i")
- self._graph.axisX().setRange(0.0, AREA_WIDTH)
- self._graph.axisY().setRange(100.0, AREA_WIDTH * ASPECT_RATIO)
- self._graph.axisZ().setRange(0.0, AREA_HEIGHT)
- self._graph.axisX().setLabelAutoRotation(30.0)
- self._graph.axisY().setLabelAutoRotation(90.0)
- self._graph.axisZ().setLabelAutoRotation(30.0)
-
- self._graph.removeSeries(self._heightMapSeriesOne)
- self._graph.removeSeries(self._heightMapSeriesTwo)
- self._graph.removeSeries(self._heightMapSeriesThree)
- self._graph.addSeries(self._topography)
- self._graph.addSeries(self._highlight)
-
- self._titleLabel.setVisible(False)
- self._graph.axisX().setTitleVisible(False)
- self._graph.axisY().setTitleVisible(False)
- self._graph.axisZ().setTitleVisible(False)
-
- self._graph.axisX().setTitle("")
- self._graph.axisY().setTitle("")
- self._graph.axisZ().setTitle("")
-
- self._graph.setActiveInputHandler(self._customInputHandler)
-
- # Reset range sliders for topography map
- self._rangeMinX = 0.0
- self._rangeMinZ = 0.0
- self._stepX = 1.0
- self._stepZ = 1.0
- self._axisMinSliderX.setMinimum(0)
- self._axisMinSliderX.setMaximum(AREA_WIDTH - 200)
- self._axisMinSliderX.setValue(0)
- self._axisMaxSliderX.setMinimum(200)
- self._axisMaxSliderX.setMaximum(AREA_WIDTH)
- self._axisMaxSliderX.setValue(AREA_WIDTH)
- self._axisMinSliderZ.setMinimum(0)
- self._axisMinSliderZ.setMaximum(AREA_HEIGHT - 200)
- self._axisMinSliderZ.setValue(0)
- self._axisMaxSliderZ.setMinimum(200)
- self._axisMaxSliderZ.setMaximum(AREA_HEIGHT)
- self._axisMaxSliderZ.setValue(AREA_HEIGHT)
-
- def adjustXMin(self, min):
- minX = self._stepX * float(min) + self._rangeMinX
-
- max = self._axisMaxSliderX.value()
- if min >= max:
- max = min + 1
- self._axisMaxSliderX.setValue(max)
-
- maxX = self._stepX * max + self._rangeMinX
-
- self.setAxisXRange(minX, maxX)
-
- def adjustXMax(self, max):
- maxX = self._stepX * float(max) + self._rangeMinX
-
- min = self._axisMinSliderX.value()
- if max <= min:
- min = max - 1
- self._axisMinSliderX.setValue(min)
-
- minX = self._stepX * min + self._rangeMinX
-
- self.setAxisXRange(minX, maxX)
-
- def adjustZMin(self, min):
- minZ = self._stepZ * float(min) + self._rangeMinZ
-
- max = self._axisMaxSliderZ.value()
- if min >= max:
- max = min + 1
- self._axisMaxSliderZ.setValue(max)
-
- maxZ = self._stepZ * max + self._rangeMinZ
-
- self.setAxisZRange(minZ, maxZ)
-
- def adjustZMax(self, max):
- maxX = self._stepZ * float(max) + self._rangeMinZ
-
- min = self._axisMinSliderZ.value()
- if max <= min:
- min = max - 1
- self._axisMinSliderZ.setValue(min)
-
- minX = self._stepZ * min + self._rangeMinZ
-
- self.setAxisZRange(minX, maxX)
-
- def setAxisXRange(self, min, max):
- self._graph.axisX().setRange(min, max)
-
- def setAxisZRange(self, min, max):
- self._graph.axisZ().setRange(min, max)
-
- def setBlackToYellowGradient(self):
- gr = QLinearGradient()
- gr.setColorAt(0.0, Qt.black)
- gr.setColorAt(0.33, Qt.blue)
- gr.setColorAt(0.67, Qt.red)
- gr.setColorAt(1.0, Qt.yellow)
-
- self._sqrtSinSeries.setBaseGradient(gr)
- self._sqrtSinSeries.setColorStyle(Q3DTheme.ColorStyleRangeGradient)
-
- def setGreenToRedGradient(self):
- gr = QLinearGradient()
- gr.setColorAt(0.0, Qt.darkGreen)
- gr.setColorAt(0.5, Qt.yellow)
- gr.setColorAt(0.8, Qt.red)
- gr.setColorAt(1.0, Qt.darkRed)
-
- self._sqrtSinSeries.setBaseGradient(gr)
- self._sqrtSinSeries.setColorStyle(Q3DTheme.ColorStyleRangeGradient)
-
- @Slot(bool)
- def toggleItemOne(self, show):
- positionOne = QVector3D(39.0, 77.0, 19.2)
- positionOnePipe = QVector3D(39.0, 45.0, 19.2)
- positionOneLabel = QVector3D(39.0, 107.0, 19.2)
- if show:
- color = QImage(2, 2, QImage.Format_RGB32)
- color.fill(Qt.red)
- file_name = os.fspath(self._data_path / "oilrig.mesh")
- item = QCustom3DItem(file_name, positionOne,
- QVector3D(0.025, 0.025, 0.025),
- QQuaternion.fromAxisAndAngle(0.0, 1.0, 0.0, 45.0),
- color)
- self._graph.addCustomItem(item)
- file_name = os.fspath(self._data_path / "pipe.mesh")
- item = QCustom3DItem(file_name, positionOnePipe,
- QVector3D(0.005, 0.5, 0.005), QQuaternion(),
- color)
- item.setShadowCasting(False)
- self._graph.addCustomItem(item)
-
- label = QCustom3DLabel()
- label.setText("Oil Rig One")
- label.setPosition(positionOneLabel)
- label.setScaling(QVector3D(1.0, 1.0, 1.0))
- self._graph.addCustomItem(label)
- else:
- self.resetSelection()
- self._graph.removeCustomItemAt(positionOne)
- self._graph.removeCustomItemAt(positionOnePipe)
- self._graph.removeCustomItemAt(positionOneLabel)
-
- @Slot(bool)
- def toggleItemTwo(self, show):
- positionTwo = QVector3D(34.5, 77.0, 23.4)
- positionTwoPipe = QVector3D(34.5, 45.0, 23.4)
- positionTwoLabel = QVector3D(34.5, 107.0, 23.4)
- if show:
- color = QImage(2, 2, QImage.Format_RGB32)
- color.fill(Qt.red)
- item = QCustom3DItem()
- file_name = os.fspath(self._data_path / "oilrig.mesh")
- item.setMeshFile(file_name)
- item.setPosition(positionTwo)
- item.setScaling(QVector3D(0.025, 0.025, 0.025))
- item.setRotation(QQuaternion.fromAxisAndAngle(0.0, 1.0, 0.0, 25.0))
- item.setTextureImage(color)
- self._graph.addCustomItem(item)
- file_name = os.fspath(self._data_path / "pipe.mesh")
- item = QCustom3DItem(file_name, positionTwoPipe,
- QVector3D(0.005, 0.5, 0.005), QQuaternion(),
- color)
- item.setShadowCasting(False)
- self._graph.addCustomItem(item)
-
- label = QCustom3DLabel()
- label.setText("Oil Rig Two")
- label.setPosition(positionTwoLabel)
- label.setScaling(QVector3D(1.0, 1.0, 1.0))
- self._graph.addCustomItem(label)
- else:
- self.resetSelection()
- self._graph.removeCustomItemAt(positionTwo)
- self._graph.removeCustomItemAt(positionTwoPipe)
- self._graph.removeCustomItemAt(positionTwoLabel)
-
- @Slot(bool)
- def toggleItemThree(self, show):
- positionThree = QVector3D(34.5, 86.0, 19.1)
- positionThreeLabel = QVector3D(34.5, 116.0, 19.1)
- if show:
- color = QImage(2, 2, QImage.Format_RGB32)
- color.fill(Qt.darkMagenta)
- item = QCustom3DItem()
- file_name = os.fspath(self._data_path / "refinery.mesh")
- item.setMeshFile(file_name)
- item.setPosition(positionThree)
- item.setScaling(QVector3D(0.04, 0.04, 0.04))
- item.setRotation(QQuaternion.fromAxisAndAngle(0.0, 1.0, 0.0, 75.0))
- item.setTextureImage(color)
- self._graph.addCustomItem(item)
-
- label = QCustom3DLabel()
- label.setText("Refinery")
- label.setPosition(positionThreeLabel)
- label.setScaling(QVector3D(1.0, 1.0, 1.0))
- self._graph.addCustomItem(label)
- else:
- self.resetSelection()
- self._graph.removeCustomItemAt(positionThree)
- self._graph.removeCustomItemAt(positionThreeLabel)
-
- @Slot(bool)
- def toggleSeeThrough(self, seethrough):
- s0 = self._graph.seriesList()[0]
- s1 = self._graph.seriesList()[1]
- if seethrough:
- s0.setDrawMode(QSurface3DSeries.DrawWireframe)
- s1.setDrawMode(QSurface3DSeries.DrawWireframe)
- else:
- s0.setDrawMode(QSurface3DSeries.DrawSurface)
- s1.setDrawMode(QSurface3DSeries.DrawSurface)
-
- @Slot(bool)
- def toggleOilHighlight(self, highlight):
- s2 = self._graph.seriesList()[2]
- if highlight:
- grThree = QLinearGradient()
- grThree.setColorAt(0.0, Qt.black)
- grThree.setColorAt(0.05, Qt.red)
- s2.setBaseGradient(grThree)
- else:
- grThree = QLinearGradient()
- grThree.setColorAt(0.0, Qt.white)
- grThree.setColorAt(0.05, Qt.black)
- s2.setBaseGradient(grThree)
-
- @Slot(bool)
- def toggleShadows(self, shadows):
- sq = (QAbstract3DGraph.ShadowQualityMedium
- if shadows else QAbstract3DGraph.ShadowQualityNone)
- self._graph.setShadowQuality(sq)
-
- @Slot(bool)
- def toggleSurfaceTexture(self, enable):
- if enable:
- file_name = os.fspath(self._data_path / "maptexture.jpg")
- self._topography.setTextureFile(file_name)
- else:
- self._topography.setTextureFile("")
-
- def handleElementSelected(self, type):
- self.resetSelection()
- if type == QAbstract3DGraph.ElementCustomItem:
- item = self._graph.selectedCustomItem()
- text = ""
- if isinstance(item, QCustom3DItem):
- text += "Custom label: "
- else:
- file = item.meshFile().split("/")[-1]
- text += f"{file}: "
-
- text += str(self._graph.selectedCustomItemIndex())
- self._textField.setText(text)
- self._previouslyAnimatedItem = item
- self._previousScaling = item.scaling()
- self._selectionAnimation.setTargetObject(item)
- self._selectionAnimation.setStartValue(item.scaling())
- self._selectionAnimation.setEndValue(item.scaling() * 1.5)
- self._selectionAnimation.start()
- elif type == QAbstract3DGraph.ElementSeries:
- text = "Surface ("
- series = self._graph.selectedSeries()
- if series:
- point = series.selectedPoint()
- text += f"{point.x()}, {point.y()}"
- text += ")"
- self._textField.setText(text)
- elif (type.value > QAbstract3DGraph.ElementSeries.value
- and type < QAbstract3DGraph.ElementCustomItem.value):
- index = self._graph.selectedLabelIndex()
- text = ""
- if type == QAbstract3DGraph.ElementAxisXLabel:
- text += "Axis X label: "
- elif type == QAbstract3DGraph.ElementAxisYLabel:
- text += "Axis Y label: "
- else:
- text += "Axis Z label: "
- text += str(index)
- self._textField.setText(text)
- else:
- self._textField.setText("Nothing")
-
- def resetSelection(self):
- self._selectionAnimation.stop()
- if self._previouslyAnimatedItem:
- self._previouslyAnimatedItem.setScaling(self._previousScaling)
- self._previouslyAnimatedItem = None
-
- def toggleModeNone(self):
- self._graph.setSelectionMode(QAbstract3DGraph.SelectionNone)
-
- def toggleModeItem(self):
- self._graph.setSelectionMode(QAbstract3DGraph.SelectionItem)
-
- def toggleModeSliceRow(self):
- sm = (QAbstract3DGraph.SelectionItemAndRow
- | QAbstract3DGraph.SelectionSlice
- | QAbstract3DGraph.SelectionMultiSeries)
- self._graph.setSelectionMode(sm)
-
- def toggleModeSliceColumn(self):
- sm = (QAbstract3DGraph.SelectionItemAndColumn
- | QAbstract3DGraph.SelectionSlice
- | QAbstract3DGraph.SelectionMultiSeries)
- self._graph.setSelectionMode(sm)
-
- def setAxisMinSliderX(self, slider):
- self._axisMinSliderX = slider
-
- def setAxisMaxSliderX(self, slider):
- self._axisMaxSliderX = slider
-
- def setAxisMinSliderZ(self, slider):
- self._axisMinSliderZ = slider
-
- def setAxisMaxSliderZ(self, slider):
- self._axisMaxSliderZ = slider
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from PySide6.QtCore import Qt
-from PySide6.QtGui import QImage, QVector3D
-from PySide6.QtGraphs import (QSurface3DSeries, QSurfaceDataItem)
-
-
-# Value used to encode height data as RGB value on PNG file
-PACKING_FACTOR = 11983.0
-
-
-class TopographicSeries(QSurface3DSeries):
-
- def __init__(self):
- super().__init__()
- self._sampleCountX = 0.0
- self._sampleCountZ = 0.0
- self.setDrawMode(QSurface3DSeries.DrawSurface)
- self.setFlatShadingEnabled(True)
- self.setBaseColor(Qt.white)
-
- def sampleCountX(self):
- return self._sampleCountX
-
- def sampleCountZ(self):
- return self._sampleCountZ
-
- def setTopographyFile(self, file, width, height):
- heightMapImage = QImage(file)
- bits = heightMapImage.bits()
- imageHeight = heightMapImage.height()
- imageWidth = heightMapImage.width()
- widthBits = imageWidth * 4
- stepX = width / float(imageWidth)
- stepZ = height / float(imageHeight)
-
- dataArray = []
- for i in range(0, imageHeight):
- p = i * widthBits
- z = height - float(i) * stepZ
- newRow = []
- for j in range(0, imageWidth):
- aa = bits[p + 0]
- rr = bits[p + 1]
- gg = bits[p + 2]
- color = (gg << 16) + (rr << 8) + aa
- y = float(color) / PACKING_FACTOR
- item = QSurfaceDataItem(QVector3D(float(j) * stepX, y, z))
- newRow.append(item)
- p += 4
- dataArray.append(newRow)
-
- self.dataProxy().resetArray(dataArray)
-
- self._sampleCountX = float(imageWidth)
- self._sampleCountZ = float(imageHeight)
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from PySide6.QtCore import QObject, Signal
-
-
-class VariantBarDataMapping(QObject):
-
- rowIndexChanged = Signal()
- columnIndexChanged = Signal()
- valueIndexChanged = Signal()
- rowCategoriesChanged = Signal()
- columnCategoriesChanged = Signal()
- mappingChanged = Signal()
-
- def __init__(self, rowIndex, columnIndex, valueIndex,
- rowCategories=[], columnCategories=[]):
- super().__init__(None)
- self._rowIndex = rowIndex
- self._columnIndex = columnIndex
- self._valueIndex = valueIndex
- self._rowCategories = rowCategories
- self._columnCategories = columnCategories
-
- def setRowIndex(self, index):
- self._rowIndex = index
- self.mappingChanged.emit()
-
- def rowIndex(self):
- return self._rowIndex
-
- def setColumnIndex(self, index):
- self._columnIndex = index
- self.mappingChanged.emit()
-
- def columnIndex(self):
- return self._columnIndex
-
- def setValueIndex(self, index):
- self._valueIndex = index
- self.mappingChanged.emit()
-
- def valueIndex(self):
- return self._valueIndex
-
- def setRowCategories(self, categories):
- self._rowCategories = categories
- self.mappingChanged.emit()
-
- def rowCategories(self):
- return self._rowCategories
-
- def setColumnCategories(self, categories):
- self._columnCategories = categories
- self.mappingChanged.emit()
-
- def columnCategories(self):
- return self._columnCategories
-
- def remap(self, rowIndex, columnIndex, valueIndex,
- rowCategories=[], columnCategories=[]):
- self._rowIndex = rowIndex
- self._columnIndex = columnIndex
- self._valueIndex = valueIndex
- self._rowCategories = rowCategories
- self._columnCategories = columnCategories
- self.mappingChanged.emit()
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from PySide6.QtCore import Slot
-from PySide6.QtGraphs import QBarDataProxy, QBarDataItem
-
-
-class VariantBarDataProxy(QBarDataProxy):
-
- def __init__(self):
- super().__init__()
- self._dataSet = None
- self._mapping = None
-
- def setDataSet(self, newSet):
- if self._dataSet:
- self._dataSet.itemsAdded.disconnect(self.handleItemsAdded)
- self._dataSet.dataCleared.disconnect(self.handleDataCleared)
-
- self._dataSet = newSet
-
- if self._dataSet:
- self._dataSet.itemsAdded.connect(self.handleItemsAdded)
- self._dataSet.dataCleared.connect(self.handleDataCleared)
- self.resolveDataSet()
-
- def dataSet(self):
- return self._dataSet.data()
-
- # Map key (row, column, value) to value index in data item (VariantItem).
- # Doesn't gain ownership of mapping, but does connect to it to listen for
- # mapping changes. Modifying mapping that is set to proxy will trigger
- # dataset re-resolving.
- def setMapping(self, mapping):
- if self._mapping:
- self._mapping.mappingChanged.disconnect(self.handleMappingChanged)
-
- self._mapping = mapping
-
- if self._mapping:
- self._mapping.mappingChanged.connect(self.handleMappingChanged)
-
- self.resolveDataSet()
-
- def mapping(self):
- return self._mapping.data()
-
- @Slot(int, int)
- def handleItemsAdded(self, index, count):
- # Resolve new items
- self.resolveDataSet()
-
- @Slot()
- def handleDataCleared(self):
- # Data cleared, reset array
- self.resetArray(None)
-
- @Slot()
- def handleMappingChanged(self):
- self.resolveDataSet()
-
- # Resolve entire dataset into QBarDataArray.
- def resolveDataSet(self):
- # If we have no data or mapping, or the categories are not defined,
- # simply clear the array
- if (not self._dataSet or not self._mapping
- or not self._mapping.rowCategories()
- or not self._mapping.columnCategories()):
- self.resetArray()
- return
-
- itemList = self._dataSet.itemList()
-
- rowIndex = self._mapping.rowIndex()
- columnIndex = self._mapping.columnIndex()
- valueIndex = self._mapping.valueIndex()
- rowList = self._mapping.rowCategories()
- columnList = self._mapping.columnCategories()
-
- # Sort values into rows and columns
- itemValueMap = {}
- for item in itemList:
- key = str(item[rowIndex])
- v = itemValueMap.get(key)
- if not v:
- v = {}
- itemValueMap[key] = v
- v[str(item[columnIndex])] = float(item[valueIndex])
-
- # Create a new data array in format the parent class understands
- newProxyArray = []
- for rowKey in rowList:
- newProxyRow = []
- for i in range(0, len(columnList)):
- item = QBarDataItem(itemValueMap[rowKey][columnList[i]])
- newProxyRow.append(item)
- newProxyArray.append(newProxyRow)
-
- # Finally, reset the data array in the parent class
- self.resetArray(newProxyArray)
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from PySide6.QtCore import QObject, Signal
-
-
-class VariantDataSet(QObject):
-
- itemsAdded = Signal(int, int)
- dataCleared = Signal()
-
- def __init__(self):
- super().__init__()
- self._variantData = []
-
- def clear(self):
- for item in self._variantData:
- item.clear()
- del item
-
- self._variantData.clear()
- self.dataCleared.emit()
-
- def addItem(self, item):
- self._variantData.append(item)
- addIndex = len(self._variantData)
-
- self.itemsAdded.emit(addIndex, 1)
- return addIndex
-
- def addItems(self, itemList):
- newCount = len(itemList)
- addIndex = len(self._variantData)
- self._variantData.extend(itemList)
- self.itemsAdded.emit(addIndex, newCount)
- return addIndex
-
- def itemList(self):
- return self._variantData
+++ /dev/null
-{
- "files": ["main.py",
- "axesinputhandler.py",
- "bargraph.py",
- "custominputhandler.py",
- "graphmodifier.py",
- "highlightseries.py",
- "rainfalldata.py",
- "scatterdatamodifier.py",
- "scattergraph.py",
- "surfacegraph.py",
- "surfacegraphmodifier.py",
- "topographicseries.py",
- "variantbardatamapping.py",
- "variantbardataproxy.py",
- "variantdataset.py",
- "data/layer_1.png",
- "data/layer_2.png",
- "data/layer_3.png",
- "data/license.txt",
- "data/maptexture.jpg",
- "data/narrowarrow.mesh",
- "data/oilrig.mesh",
- "data/pipe.mesh",
- "data/raindata.txt",
- "data/refinery.mesh",
- "data/topography.png"
-]
-}
+.. _rhi-window-example:
+
RHI Window Example
==================
for the window are all managed by Qt Quick. Whereas in this example, all that
is managed and taken care of by the application itself.
-.. note:: For ``QWidget``-based applications in particular, it should be noted
- that ``QWidget::createWindowContainer()`` allows embedding a ``QWindow``
- (backed by a native window) into the widget-based user interface. Therefore,
- the ``HelloWindow`` class from this example is reusable in ``QWidget``-based
- applications, assuming the necessary initialization from ``main()`` is in place
- as well.
-
+.. note:: For ``QWidget``-based applications, see the :ref:`rhi-widget-example`.
Shaders
-------
self.m_audioSink.start(self.m_generator)
self.m_volumeSlider.setValue(self.m_audioSink.volume() * 100)
- @Slot(int)
- def device_changed(self, index):
+ def closeEvent(self, e):
+ self.stop()
+ e.accept()
+
+ def stop(self):
self.m_pullTimer.stop()
self.m_generator.stop()
self.m_audioSink.stop()
+
+ @Slot(int)
+ def device_changed(self, index):
+ self.stop()
self.m_device = self.m_deviceBox.itemData(index)
self.create_audio_output()
is_android = os.environ.get('ANDROID_ARGUMENT')
-if is_android:
+if is_android or sys.platform == "darwin":
from PySide6.QtCore import QMicrophonePermission
@Slot()
def initialize(self):
- if is_android:
+ if is_android or sys.platform == "darwin":
+ is_nuitka = "__compiled__" in globals()
+ if not is_nuitka and sys.platform == "darwin":
+ print("This example does not work on macOS when Python is run in interpreted mode."
+ "For this example to work on macOS, package the example using pyside6-deploy"
+ "For more information, read `Notes for Developer` in the documentation")
+ sys.exit(0)
permission = QMicrophonePermission()
permission_status = qApp.checkPermission(permission) # noqa: F821
if permission_status == Qt.PermissionStatus.Undetermined:
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import os
+import sys
from pathlib import Path
from PySide6.QtMultimedia import (QAudioInput, QCamera, QCameraDevice,
from imagesettings import ImageSettings
from videosettings import VideoSettings, is_android
-if is_android:
+if is_android or sys.platform == "darwin":
from PySide6.QtCore import QMicrophonePermission, QCameraPermission
+
+if is_android:
from ui_camera_mobile import Ui_Camera
else:
from ui_camera import Ui_Camera
@Slot()
def initialize(self):
- if is_android:
+ if is_android or sys.platform == "darwin":
+ is_nuitka = "__compiled__" in globals()
+ if not is_nuitka and sys.platform == "darwin":
+ print("This example does not work on macOS when Python is run in interpreted mode."
+ "For this example to work on macOS, package the example using pyside6-deploy"
+ "For more information, read `Notes for Developer` in the documentation")
+ sys.exit(0)
+
# camera
cam_permission = QCameraPermission()
cam_permission_status = qApp.checkPermission(cam_permission) # noqa: F821
################################################################################
## Form generated from reading UI file 'camera.ui'
##
-## Created by: Qt User Interface Compiler version 6.5.0
+## Created by: Qt User Interface Compiler version 6.7.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self.tab_2.setObjectName(u"tab_2")
self.gridLayout = QGridLayout(self.tab_2)
self.gridLayout.setObjectName(u"gridLayout")
- self.verticalSpacer_2 = QSpacerItem(20, 161, QSizePolicy.Minimum, QSizePolicy.Expanding)
+ self.verticalSpacer_2 = QSpacerItem(20, 161, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
self.gridLayout.addItem(self.verticalSpacer_2, 3, 0, 1, 1)
self.gridLayout_2.addWidget(self.stopButton, 2, 0, 1, 1)
- self.verticalSpacer = QSpacerItem(20, 76, QSizePolicy.Minimum, QSizePolicy.Expanding)
+ self.verticalSpacer = QSpacerItem(20, 76, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
self.gridLayout_2.addItem(self.verticalSpacer, 3, 0, 1, 1)
self.stackedWidget = QStackedWidget(self.centralwidget)
self.stackedWidget.setObjectName(u"stackedWidget")
- sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
+ sizePolicy = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred)
sizePolicy.setHorizontalStretch(1)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.stackedWidget.sizePolicy().hasHeightForWidth())
self.gridLayout_4.setObjectName(u"gridLayout_4")
self.lastImagePreviewLabel = QLabel(self.previewPage)
self.lastImagePreviewLabel.setObjectName(u"lastImagePreviewLabel")
- sizePolicy1 = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.MinimumExpanding)
+ sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.MinimumExpanding)
sizePolicy1.setHorizontalStretch(0)
sizePolicy1.setVerticalStretch(0)
sizePolicy1.setHeightForWidth(self.lastImagePreviewLabel.sizePolicy().hasHeightForWidth())
################################################################################
## Form generated from reading UI file 'camera_mobile.ui'
##
-## Created by: Qt User Interface Compiler version 6.5.2
+## Created by: Qt User Interface Compiler version 6.7.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self.gridLayout_3.setObjectName(u"gridLayout_3")
self.captureWidget = QTabWidget(self.centralwidget)
self.captureWidget.setObjectName(u"captureWidget")
- sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
+ sizePolicy = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.captureWidget.sizePolicy().hasHeightForWidth())
self.label = QLabel(self.tab_2)
self.label.setObjectName(u"label")
- sizePolicy1 = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
+ sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
sizePolicy1.setHorizontalStretch(0)
sizePolicy1.setVerticalStretch(0)
sizePolicy1.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
self.verticalLayout_2 = QVBoxLayout()
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
- self.verticalSpacer = QSpacerItem(20, 10, QSizePolicy.Minimum, QSizePolicy.Expanding)
+ self.verticalSpacer = QSpacerItem(20, 10, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
self.verticalLayout_2.addItem(self.verticalSpacer)
self.stackedWidget = QStackedWidget(self.centralwidget)
self.stackedWidget.setObjectName(u"stackedWidget")
- sizePolicy2 = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ sizePolicy2 = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
sizePolicy2.setHorizontalStretch(1)
sizePolicy2.setVerticalStretch(0)
sizePolicy2.setHeightForWidth(self.stackedWidget.sizePolicy().hasHeightForWidth())
self.gridLayout_5.setObjectName(u"gridLayout_5")
self.viewfinder = QVideoWidget(self.viewfinderPage)
self.viewfinder.setObjectName(u"viewfinder")
- sizePolicy3 = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ sizePolicy3 = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
sizePolicy3.setHorizontalStretch(0)
sizePolicy3.setVerticalStretch(0)
sizePolicy3.setHeightForWidth(self.viewfinder.sizePolicy().hasHeightForWidth())
self.gridLayout_4.setObjectName(u"gridLayout_4")
self.lastImagePreviewLabel = QLabel(self.previewPage)
self.lastImagePreviewLabel.setObjectName(u"lastImagePreviewLabel")
- sizePolicy4 = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.MinimumExpanding)
+ sizePolicy4 = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.MinimumExpanding)
sizePolicy4.setHorizontalStretch(0)
sizePolicy4.setVerticalStretch(0)
sizePolicy4.setHeightForWidth(self.lastImagePreviewLabel.sizePolicy().hasHeightForWidth())
################################################################################
## Form generated from reading UI file 'imagesettings.ui'
##
-## Created by: Qt User Interface Compiler version 6.5.0
+## Created by: Qt User Interface Compiler version 6.7.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self.gridLayout.addWidget(self.groupBox_2, 0, 0, 1, 1)
- self.verticalSpacer = QSpacerItem(20, 14, QSizePolicy.Minimum, QSizePolicy.Expanding)
+ self.verticalSpacer = QSpacerItem(20, 14, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
self.gridLayout.addItem(self.verticalSpacer, 1, 0, 1, 1)
################################################################################
## Form generated from reading UI file 'videosettings.ui'
##
-## Created by: Qt User Interface Compiler version 6.5.0
+## Created by: Qt User Interface Compiler version 6.7.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self.widget = QWidget(VideoSettingsUi)
self.widget.setObjectName(u"widget")
- sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
+ sizePolicy = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.widget.sizePolicy().hasHeightForWidth())
self.gridLayout_3.addWidget(self.widget, 2, 0, 1, 1)
- self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
+ self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
self.gridLayout_3.addItem(self.verticalSpacer, 3, 0, 1, 1)
################################################################################
## Form generated from reading UI file 'videosettings_mobile.ui'
##
-## Created by: Qt User Interface Compiler version 6.5.2
+## Created by: Qt User Interface Compiler version 6.7.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self.gridLayout_3.setObjectName(u"gridLayout_3")
self.widget = QWidget(VideoSettingsUi)
self.widget.setObjectName(u"widget")
- sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
+ sizePolicy = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.widget.sizePolicy().hasHeightForWidth())
self.addToolBar(tool_bar)
file_menu = self.menuBar().addMenu("&File")
- icon = QIcon.fromTheme("document-open")
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentOpen)
open_action = QAction(icon, "&Open...", self,
shortcut=QKeySequence.Open, triggered=self.open)
file_menu.addAction(open_action)
tool_bar.addAction(open_action)
- icon = QIcon.fromTheme("application-exit")
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.ApplicationExit)
exit_action = QAction(icon, "E&xit", self,
shortcut="Ctrl+Q", triggered=self.close)
file_menu.addAction(exit_action)
play_menu = self.menuBar().addMenu("&Play")
style = self.style()
- icon = QIcon.fromTheme("media-playback-start.png",
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.MediaPlaybackStart,
style.standardIcon(QStyle.SP_MediaPlay))
self._play_action = tool_bar.addAction(icon, "Play")
self._play_action.triggered.connect(self._player.play)
play_menu.addAction(self._play_action)
- icon = QIcon.fromTheme("media-skip-backward-symbolic.svg",
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.MediaSkipBackward,
style.standardIcon(QStyle.SP_MediaSkipBackward))
self._previous_action = tool_bar.addAction(icon, "Previous")
self._previous_action.triggered.connect(self.previous_clicked)
play_menu.addAction(self._previous_action)
- icon = QIcon.fromTheme("media-playback-pause.png",
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.MediaPlaybackPause,
style.standardIcon(QStyle.SP_MediaPause))
self._pause_action = tool_bar.addAction(icon, "Pause")
self._pause_action.triggered.connect(self._player.pause)
play_menu.addAction(self._pause_action)
- icon = QIcon.fromTheme("media-skip-forward-symbolic.svg",
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.MediaSkipForward,
style.standardIcon(QStyle.SP_MediaSkipForward))
self._next_action = tool_bar.addAction(icon, "Next")
self._next_action.triggered.connect(self.next_clicked)
play_menu.addAction(self._next_action)
- icon = QIcon.fromTheme("media-playback-stop.png",
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.MediaPlaybackStop,
style.standardIcon(QStyle.SP_MediaStop))
self._stop_action = tool_bar.addAction(icon, "Stop")
self._stop_action.triggered.connect(self._ensure_stopped)
self._volume_slider.valueChanged.connect(self._audio_output.setVolume)
tool_bar.addWidget(self._volume_slider)
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.HelpAbout)
about_menu = self.menuBar().addMenu("&About")
- about_qt_act = QAction("About &Qt", self, triggered=qApp.aboutQt) # noqa: F821
+ about_qt_act = QAction(icon, "About &Qt", self, triggered=qApp.aboutQt) # noqa: F821
about_menu.addAction(about_qt_act)
self._video_widget = QVideoWidget()
self._screen_label = QLabel("Select screen to capture:", self)
self._video_widget_label = QLabel("Capture output:", self)
self._start_stop_button = QPushButton(self)
+ self._status_label = QLabel(self)
self._screen_list_model = ScreenListModel(self)
grid_layout.addWidget(self._video_widget, 1, 1, 4, 1)
grid_layout.addWidget(self._window_label, 2, 0)
grid_layout.addWidget(self._window_list_view, 3, 0)
+ grid_layout.addWidget(self._status_label, 5, 0, 1, 2)
grid_layout.setColumnStretch(1, 1)
grid_layout.setRowStretch(1, 1)
@Slot(QItemSelection)
def on_current_screen_selection_changed(self, selection):
+ self.clear_error_string()
indexes = selection.indexes()
if indexes:
self._screen_capture.setScreen(self._screen_list_model.screen(indexes[0]))
@Slot(QItemSelection)
def on_current_window_selection_changed(self, selection):
+ self.clear_error_string()
indexes = selection.indexes()
if indexes:
window = self._window_list_model.window(indexes[0])
@Slot(QWindowCapture.Error, str)
def on_window_capture_error_occured(self, error, error_string):
- QMessageBox.warning(self, "QWindowCapture: Error occurred",
- error_string)
+ self.set_error_string("QWindowCapture: Error occurred " + error_string)
@Slot(QScreenCapture.Error, str)
def on_screen_capture_error_occured(self, error, error_string):
- QMessageBox.warning(self, "QScreenCapture: Error occurred",
- error_string)
+ self.set_error_string("QScreenCapture: Error occurred " + error_string)
+
+ def set_error_string(self, t):
+ self._status_label.setStyleSheet("background-color: rgb(255, 0, 0);")
+ self._status_label.setText(t)
+
+ def clear_error_string(self):
+ self._status_label.clear()
+ self._status_label.setStyleSheet("")
@Slot()
def on_start_stop_button_clicked(self):
+ self.clear_error_string()
self.update_active(self._source_type, not self.is_active())
def update_start_stop_button_text(self):
--- /dev/null
+# Resource object code (Python 3)
+# Created by: object code
+# Created by: The Resource Compiler for Qt version 6.4.0
+# WARNING! All changes made in this file will be lost!
+
+from PySide6 import QtCore
+
+qt_resource_data = b"\
+\x00\x001G\
+\x1f\
+\x8b\x08\x00\x00\x00\x00\x00\x00\x00\xec}\xebs\x1b\xc7\xb1\
+\xef\xe7\xe4\xaf\xe0U\xbeDU\xc0\xb0\xdf\x0f%\xce\xa9\
+\xc4\x8eS\xa9r\xea\xa4n\x1e\xf7\xdcO.\x88\x04%\
+\x1e\xd3\xa4\x0eI\xf9\x91\xbf\xfe\xee\x02$g!h\xb1\
+\x00\x16P$_s\x5c\xf2b\xb6\xb7{\xfa7==\
+=\xb33\xb3\xbf\xfd\x8f\x1f\xbe\xbd:\xf9n~{w\
+ys\xfd\xd93,\xf0\xecd~}vs~y\xfd\
+\xea\xb3g\xff\xf8\xfb\x97\xd3xvrw?\xbb>\x9f\
+]\xdd\x5c\xcf?{v}\xf3\xec?~\xf7\xcb\xdf\xfe\
+\xaf\xe9\xf4\xe4O\xf3\xeb\xf9\xed\xec\xfe\xe6\xf6\xc5\xc9\xef\
+\xcfo^\xceO\xfe|u\xf5\xf6\xee~\x91u\x82T\
+\xa0\xc0\xe4\xe4o\xff\xfc\xd3\xc9\x1f\x7fxss{\x7f\
+\xf2\xd7\xab\xb7\xaf\xa6\x7f\xbe>)\x8b\xcc\x7f.e\xbe\
+8\xb1\x02p\xf2\x87\xb7\x97W\xe7'\x8a\x22\xf1\xfc\xe4\
+d:mD\xdc}\xf7\xea\x97'''M\xf9\xae\xef\
+^\x5c\xdf}\xf6\xec\xf5\xfd\xfd\x9b\x17\xa7\xa7\xd7we\
+\xd6\x8a+g7\xdf\x9e\xfem\xf6\xdd\xfc\xcb\x9b\xdb\xff\
+3\x7fy\x8a\x05N\x9f\xd5'f\xef\x7f`Q\xd2F\
+\xfe?/\xe7\xdf\xcfo\xff\xf8\xc3\xfd\xfc\xba-\xc6\xdd\
+)\xaf>~\xb9\xe1\xf1\x8e\x9e\xa7\x08\xab\xcf\x9d\x9f=\
+=\xf8\xe6\xed\xedU\xb9\xb9}uz~v:\xbf\x9a\
+\x7f;\xbf\xbe\xbfkJ\x89]\xf2\xb3J~v;\x9f\
+\xdd_~7o\xc4|\xdb\x14\xa8}\xb2\x91\xfd\xab\x0e\
+\xf1\xed\xf9\xc5\x13\xf5\xf7\xdf\x7f_\xbe\xe7\x05\x11f\xe6\
+)\xd0)\xd1\xb4\xa1\x98\xde\xfdx}?\xfba\xba\xfa\
+h\x03\xe6\xfb\x1e%\x008m\xeeU\xca\xed\xa8^\xfc\
+puy\xfdMoa\x16w\xbb\xd2\x1b{zss\
+^1}\xcc(w7oo\xcf\xe6\x17\xcd\x93\xf3r\
+=\xbf?\xfd\xe2\xef_<\xdd\x9cB9\xbf?\xef\xb0\
+i\x98\xde\x9d\xcd\xde\xccW\xe4>f.\xf1\x9a};\
+\xbf{3;\x9b\xdf\x9d>\xe6\xb7\xcfw\x0d\x1c\x17\x19\
+\x97\xe7\x9f=\xfbj\xf6\xe3\xfc\xf6\xeb\xe5\xef\xef/\xcf\
+\xef_7\xb7)\x16?_\xcf/_\xbd\xbe\xaf\xbf\xbf\
+k\xac\xe5\x0f7?|\xf6\x0cN\xe0\x04)N\x1eo\
+\xdc4\x9c/\xaen\xbe\xff\xec\xd9w\x97w\x97/\xaf\
+\x96\xe2\xe6\xd7\xb3\xe6r\xfarv\xf6\xcd\xab\xdb\x9b\xb7\
+\xd7\x8d\xb0\xeb\xf9\xf7'\x9d\x87\x1f\xb5z\xd1\x96\xb6Q\
+\xe8\xcd\xed\xfcn~\xfb\xdd\xf2\xf1G\x00^<\x15\x1b\
+\x0a\xd3\xe2\xd6\xa3V\xdd[b\xabO\x9d\xdf\x9c58\
+4L_\xddL\xaf\xe7?\xdcO\xdf\xcc\x1at\x9b\xfa\
+\xfb\xd7\x1a\xdd\xcb\xd9]Cw\xfa\x8f\xbb\x86\xdb\xe9\xf9\
+\xec\xbb\xcb\xf3\xd3/\xe6w\xdf\xdc\xdf\xbc9\xbdk\xda\
+\xfc\xcb\x9b\x1fV\xa5\xde\xbc\xbd\x7f\xf3\xf6\xfe\xeby\xdb\
+l\x96\xe2\x1b\xd4k\x15,o/D=e\xae2\x98\
+/\x5c\xc1\xf4\xe2\xf2j\xbe,\xe3\xe9\xeb\x9bo\xe7\xa7\
+o.\xaf\x1b\xc8oo\x9a\x8b\xb3\xbb\xd3\x9b\x1f~|\
+5\xbf>m\x9e\xb8jq<\x9d\x9d\xdd/\x9a\xe8\x8a\
+>o\xae_\xbd\x97\xf5\x0f\xe7o\x1a;3/\xfa\xde\
+\xdb?\xd6\xdb\xbf\xfb\xed\xf9\xfc\xe2\xee\xd1\x1a\xdak\x84\
+6\xf7\xe9\x917\x0d*o\xe6gm\xa3\x5c\x01\xee\xfe\
+\xc7\xd6\x06W\xc9\xf8\xfc\x9d\xfay\xf3uc.S\x85\
+\x93\xc6\xc3A\xfb/\xae\x11\xfc\xd8\xd4^{\x03\x16\xf7\
+a\xed\xfe\xbf>{\xe6\xb0\xce`U\xec\xf4\xe6\xf6\xf2\
+\xd5eS\x13\xbc \x92J\xbaP\xab\xa3\x03!=;\
+9\xfd\xe9\xea\x87\xa6\x0b\xfd\xbe\x9d\xdd~3\xbf]\xe1\
+uw\x7fs\xf6MK\xfe\xfb\xdb\xdb\x9b\xef\xf1/\xf3\
+\xeb\xa5:\x0d\xef\xf9u\xd3\xcego\xefo\x16\x19\xb7\
+\xf3\x8b\xff\xdb\xb6)x\xfc\xf5_\xf5\xd7{\x9e\xbf\xbb\
+\xff\xf1\xaa\x81\xea\xd1\x0b\xbcxp\x02\xbfi\xac\xe8\xcd\
+\xec\xfeuK\xb2|\xae\xfd\xc5\xac\xfel\x99\xd5\xe4\xfc\
+\xe5\xa4\xed\x1f\xa1\xc0\xc9W'\xda\x5cMuq9E\
+*\xda\xc9^\xe4>\x91\xfe\xeb\xa4eP\x057\xed\xe8\
+jz\xfb\xf6\xaa1\xef\xef\xe6\xd77\xe7\xe7\xbfi\xfa\
+\xa5\x9bo\xe6/~\x05\x8b\xbf\x87\x9f\xd3\x85{{\x81\
+\x05\xde\xdc\xfff\x09\xcf\xb4\xe9\xd4o\xef_\x5c7\x9d\
+\xfao\x1ex6=\xda\xf5]\xe3\x8a\xbf\xfd\xecY\xdb\
+\xf4\xe6\xbf\x86\x22\xcfOno\xeeg\xf7\xf3_c\xc0\
+\xf3%\xc5\xd5\xe2'L\xe0\xf9\x02\xed\xd3%\xbf\xad`\
+_\xc8\x1c\x01|\xe5\xd0\x0b}\x1f\xf2\xf21\x22\xbf\x01\
+\xf8\xd1`\xffm4\xd8\x7f\xdb\x17l\x83O\x0bl\xea\
+\x82m{`\xfd\xd5H\x7f\xf2\xd5\xbe\xfe\x04?1\x7f\
+\x12\xbd\xfe\x84\x8a\xee\x03\xfch#\xffj_#\x97\xf8\
+\xb4\x8c<\x06\xe0>h\x5c\xb0\xe8\xd4e\xdf\xa0\x00)\
+\xea\xf3\x1bc\x82\x05\x8dP\xb1\xf6\xcf{\x03\x83\xf0\x85\
+\xa6\xcd\xa8d>\xbb\xfd\xd3\xed\xec\xbc5\x95\x15\xc6g\
+7WW\x0dqc>W\xdf\xcf~\xbc{\xe2\xb2\xfa\
+\x88!rc\x0e\x8d\x11\xbeY\xa9\xba6c\xda\xb0\xb8\
+\xb9\xad\x95\xd5f\xdd4\xc1\xfd\xe5\xfd\x8f/\xf0\xb1I\
+\xdc\x5c\x5c\xdc\xcd\x1b)\xf0\xac\x9aSKi\x88m\xec\
+\xb2\x07kx\x975\xae\xb3\xf6e=\xaf\xea\xf2\xbb\xdf\
+\xb6\x17\xb3\xab\xad\xe1X\x0c\xea^\xbc\xbe\x9d7\x83\xd0\
+_\xad\xf2b\x02\x7f\x82l\x95-Q\xe8\xe2\xd6\xab\x87\
+\x8c\x7f\x5c_\xde7\xa3\xcc\xb7\xcd\x90\xe3o\xed\xd8\xe7\
+?\xaf\x9b\xd1\xc7\x0a\xc5\xdf\xab\xf1~;\xbb\xbf\xbd\xfc\
+\xe1\xd7X@\x88T&\xd0\xa6\x82d\x802!,\x9a\
+\xed\xc54\xa8\x18z\xe2\xf3\x05\x9f\xb3\xc6\x00%\x0b \
+\xa6\xe32\xa71:*\xe6\xc2\xe0\xb1\xc8\xb9X\xa3\xb9\
+X\xa3\xb9m\x87\x8bNb\x98\xdcc?\xeb&B\xc4\
+4d\x22/%\x85f;\x9a\x08\x11\xcb\xb0\x89\xcc\xe5\
+\xfc\xec,\xdfa=`!Dl\xef\xb7\x90\xfa{\x84\
+\x85TX\xfa\x10\x0b\x1em!P<\x125\x17\x16\xf2\
+\xf4\x83\xb0\x80\x85\xca\x84\x8b\xa4\x13\xc5|J\xcf\x97E\
+\xc5E\xfd\xa32,\x85\xff\xd8d\xb8\x16F`\x8c%\
+\x09}\xf6,\xa8H\x88\x09-i\xa8C\xb3\xb5E0\
+\xb1>\xdb\x88]\xad\xd3Z)L\xeck\x16\xd1[\xed\
+\x17\x8b\xbf5\x8b:9\xeda\x9dk&\xb1\x13k\xf8\
+\xcd\xf1\xec\xa5\x82\xd6o/x\x00\x8f\x82H\xc29\xc1\
+\x02\xac\xc26\x99\xb6Wh\xc6\x93\xf6\x9e)\xf8\xc4\xb4\
+\xa8\x83\xf2d*YP\x08\xa5c<Q$C\x8d\x9e\
+\xac\x07J *H\xda\x93\xf9H\x14gr\xe2'\xf3\
+\xa1\xc2\xa6N\x96;\x98\x0fx\xb5\x90\x81:\xda\xc5\xa1\
+0A\x0e;\x14\xd26\xed\xd8\xe70!\xfe[\xfb\x1c\
+\xcf\x03X\x88bz\xedsBRh\x82R\x02\x12\x1a\
+\x93p/\x88\xea\x1fC\x9f\xc3Dz$\x13!\x1f6\
+\x91\xd9\xbcM\x03\xacq\x9du\x1e\xdfDH\xfbM\xc4\
+\x8f\xe1D\xa0p\x84yL\xa0\x08\x81\x89L\x0c\x0aB\
+\xd0d*\x5c\x88Am\xd5` #\xe4\xc9`\xa2H\
+*#Q5\x18/\xec\x10\x11O\x06c\xc5\x1d\xd0\xd2\
+\x1f\x0d\x86K\xba\x82\x80no0L\xfa\xfe^\x87\xe9\
+h\xbd\x0e\xd3\x11{\x9da\x8d\x81\xf7i\x22P,X\
+\x94\xc9\x06\xdb\x0a/*`\x0f\x19\xc3\xee\x94\xe1\xf8!\
+<\x03\xf7\xb7\x95C\x04h\x99\x99m\xb0\xee%\x04M\
+l>\xe5\x89\x163\x03\xcf\xf9T&T@\x82\xa1\xc9\
+\xa7\xb63&!bi\x89\xbch \xab\xd7\x86\xc3R\
+\xcc\xdd8\x9f\x1a\x8e\x171\x22\xb7e\xce\xc5\x1a\xcd\xc5\
+\x1a\xcd\xed\xa2mi*f\x0b\xeeA\x83\x17\xd0\xe8\x0d\
+^\x1c\x0f\x80e\xa8)P\x1b\xe8f\xea\xd2\xefx\x9a\
+Q\x9b\x15\x9a\xc62A\xd4\xa2H<\x99\x12\x96d\xf1\
+\xac\xc1\x0bba2\x0ey\x0a^\xd4\x0b\x07g\xe6S\
+\xec\x82R$Y\x19\x974Tiv\x88]@\xe3\xfd\
+~\x06\x0c\xb6\xf739s[\x1fRo1\x18\xe2\xd9\
+\xf9\xf0P\xbc@R\x12\xb0=[+\xa3\xf5\xf92p\
+Xc\xa3 \x8c\xbe\xd1\xa7\x9d\x9d\xd3\xd9\xb6\xee\x12,\
+\xd6D$\xaa#\x87>\x1bPy{\x19\xb4\xbdK~\
+\x99\xd1\xc3\xfa\xa8~\x094z\xfd\x92\x8d\x0f\xf3\xb4\x90\
+9\x0b\xb6^G\x0b(\x1a\xca\x83\x07\x02uOY\x0e\
+\x07\x88\x9b\x8b\xe0\xe2\xae8\x99\x9a\x15\xe5\xd0\xe8x$\
++\xe6j\x12O\x1e)\xb5\xb8%y\xf5Hh\x85\xd0\
+Y\xa5v\xe5RP\x90\x02;\x1e\xc9\xc5\x04v\x98o\
+\xd0|\x7f\x13#\x83\x1d\x9aX\x0d\xf3\x87\x9b\xd8\xf0s\
+\xebm\xe3\xd9Z\xe1\xfa\xda\x16\x19\xac\x1b\xbe\xeb\xb8\xa2\
+w\xb9\xef`\xf2\x00\x00\x1f\xde\xe4Is\x83\xc9\x1f\x22\
+lU\x09\xe5\x09\x16\xb1D\x94E\x0f\x01\x8a\x16\x13\xa2\
+b\xa1\x19\x13$\xa7\x92\xd2\x0e\x8b)\xb1\x08P\xb5u\
+\xd4\xe2\xe4\xe0\xfed\xeb\x1a\x05\x22-\xbc\xda\xfa\x0a\xcd\
+\xc5\x1a\xcd\xedb\xe0\x83\xc2i\xba\xc3\xdc\x9a\xfa\x91\xe6\
+\xd64\x8f5\xb7fxt{!\xf5~{9\xc4\xec\
++1\x1a\xda\x04\x0b%\x0aac\x14X\x1c\x16YX\
+\xd0\xa3u\x8e\x82X\xc2\xa5\xb9\xc7\xa4E\xb5c0B\
+\xc5\xd0\x95\xeb\xc0\x18\x09\x8a\x09>\x8ej\xd6h.\xba\
+4u\x9c#`\x11\xbb\xcc\xbc%\x0e\xd8\xcb\xcb\xabf\
+\x8d\xceZ\x95\x0e\x0c\x8a\x17C\xf3\x9d\xf9\xc2\xe0\x888\
+u\xdb\x01N\x1b\x8e\xc1S\xa8F\xd1\x8d\xe3\xf4)\xcc\
+\xa3\xd8\xb2\xee\xd7\xa1\xc30k\xfa\x95_\xfe\xe2\x17\xbf\
+x\xd0\xb3\x16\x14\xc3\xda\xf0\xa7w\x80sv\xf6\xdey\
+\x86\x0ah\xa3e?g\xea\xe1\xfc\xfd\xeb\xcb\xfb\xf9\xfa\
+\xc0\xa9\x0b\xe8\x03\xdf\x8f\x03@M\xf3>\x005-\x07\
+\xd4\xdc\x0b>M\xc7#\xc3W\xdb\xda\x7f\xfd\xe5\xab?\
+\x7f\xf15}\xbd\x1dD\x15\xcf\x8as\x17\xffZ)U\
+\xb7n\xe9z\x0d\xee\xcb/\xff\xf8{\x80g+8\xd0\
+\x1aDU\xc9^6\x9f\x7f\xfe.\x1b\x19@d?\xe7\
+\xbd\xc4-\xbf\xee\x1b!r\xe2\x0e\x90\xa6\x14\x16\xae\xb8\
+\x22Pa\x11\xaf\xaf0\xac(\xab=\x01\x9cR\x94\x1f\
+F6\xe3\xd5@\xd8\xa0\x07\xef\xa2\x87vT\xe0\x95\xd2\
+\x87\xe9J\xe9\xc3\xf4P\xa5\xc7\x0d\xa5\xd7\x03\x94\xde\x0b\
+%\xd7\xc2k\xfb\xf3Pe\xa7\x0de\xf7]\xcan\x9d\
+\xb2K-{\x14\x00\xa0Zx[\xfe>9\xad\xb1\xcb\
+\xfe\xa5\xf7\xaf\xfb\x22\x16\xd9&b\xa9\xb1(P\x0d*\
+\x90\x0a\x83<\x05\x98\xc8YTC\x0fT\xe4\xd8Pd\
+?p\x91s\xe9y~\xdb\xc4a\xcdJ\xe4\xeb\xff|\
+\xf9\xdfM\x01\x174\xf3\xffy{y;?\xaf\x0b\xd7\
+w_\xa7\xfe\xe8L\x1b\xf9\xb0\xb2\xf8yu\xe9\xf3\x93\
+\xb6+\x85X\xf6i\xbf\xbd|\xf1\xe6\xd5\xc5\xff\x9e_\
+\xaca\xb5(\xc2\xd7\x97U\xf2\xd7\x0d\xe1\xf2\x99\xd3\xc7\
+\x87Z\xcdNW\xb869\xbf<\xa6SEb\x87O\
+\xdb\xabVE\xe8St\xab\xb5\xf8\xf2\xe9\xf9\xd5Zx\
+;\xaec\x1d_\xfc\x8d\xa5\x8f\x03\xc7j\x07\x9e7'\
+\x86\xec+\xbd`\xec\xd2\x80\xa7.%\x08\x1c\xbcS\x07\
+P\x82\xd8\xeb\xea\x8e)FA\xc2\xac\x0d\xa1\x12}X\
+\xd5\xf2\xd3T\xad\x8et\xfaGA\xce\xbb\x18\x9d\x17\xc7\
+t\xa9-\xc7K\x04\xd6\xe6\xb3F\xd2\xe40\x14\x11\xc4\
+\xd0\xbey\x8c\xce\xba\xc7\x22\x0e\x826\x9f\xb6\xcb\x1f?\
+`\x1dk\xba\x7f\xd0:\xae1\xcf\xe1\xebx\x95/j\
+\x86=E7d%U\xba1\x0ec\x01Q\xd0e)\
+/\xd6h.\xd6h\x9a@(JZ\xa0\xf2\xf0\xb2/\
+\x95\xf4\x98L\xa9\xa4&\xc7b\x1e\x1f&P\x12\xd4\x9d\
+&^L\x81\x5c'\x08%<\xc0\x9f\x0f\xd7\xc2\xd1=\
+q\x98\xfa\x11<q\x1bc\xadJ\xea\x95/\xf9\x11\x8e\
+\xda\xc3\x14G\x0e\xdc+'\xee\x19\xbb\xb7yM\xf8y\
+@\xf7\x17f\xd1_\xd3.\xbb4{\xce\xa2\x8cT\xa3\
+\x9e\xda\xdc\xeb\xcd\xfaB\x16\x0b\x86\xeb\xf1\xed\xd5E\x8e\
+`\xaf\x1f\xac\x12\x5c\xec\xd8\x95p\xf4A\xa6i\xc8!\
+\x16ax8\xf1\xe3\xa2\xb6\x0cl\x12M\xb2\xb0\xa1\x03\
+\xe1\x04\x0b\x1a7I\x9e\xef6^=\xf6\xb4\x80i\xf8\
+\xfe\xea\xd7\x00 \x9an\x7f\xe4\xe4\xc1xC\xed\xd11\
+\x95\xa0\xbeP\x86\x02\x08\xc8Y\xfbQ)\xae\x80R_\
+(\xaf\xd2\x5c\xac\xd14j\x10\x14\x0f$\x1dz\xc5\xb3\
+0\x08,\x94h\x08\xadyLQ\x8bzrl\xd1a\
+\x1e}N\x88>\xcd\xca\x1f\x9e9\xa2\xc8\x8f\xbbQ\x1f\
+w\xaa\x9b~\x22S\xdd\xf4IOu\xd3'<\xd5M\
+G\x9c\xea>~\xebw:Z\xeb\x9fb\x14\xe3\xa0\xb4\
+\xc9\xd4J\xb0\xb5\xe9#\xeb\xd4\x89\x5c\x0f\xe1\xd7\xa7\x98\
+%\xdb\xbf\x98Lc\x0f\x0f\xdf\xec\xbc\xbc\x9f\xdfn\xb5\
+\x1fpI\xca$\xf8\xacyn\xfe\xa7\xd9\xdb\xbb\xbb\xcb\
+\xd9\xf5\x1f\xae\xde\xb6\xcf\x0fqh\x070\xe7_\xcc\xbf\
+\xbb\x9c\xdd?\x1e\xb7\xc3)\x8a\xd2Y\x0a\xb0\xca\x94I\
+x\xb9(`)\xf9\xa8\xf5! v\x88\xfa\xa0\x22\xd0\
+\xfe\xe1|j\xbbv\xb9\x07\x1e\x18\x18\x22\xf7o\xe6\xcc\
+:f\xe1\x22\xe1Fu3\x98x\x81\x8c\xf0\xda\xbd`\
+1\xf5\xb4\xac\x1d\x0ceAb%\xdbkRa\x10G\
+\xe3\x92\x92\x16-\x88O2\xa7X\x04\xc4M\xa2\xfa\xaf\
+B\x1c\x86\x91O\x85\x9f\x22\x970\xc9\xa4G\x0d+\xd1\
+\xdeK\x1a\x982\x86\xb7\x5cDl\xde\xec\xb6\x8eC\x9d\
+\xbc\xf2\x92*Y;\x15\xe7\x22F\x16\x9d.\xc7K\xfb\
+\x1b\xa3\xea\x05\x85\xc5\x1c\x87\xd7L33?\xf9\xc9h\
+\xff\x12'\x98\xc5\xc5\xc3x\xc2X C\xd9\x9f\xef\x8d\
+\x10\x11\xf8 BL\xbc?BYB\x12\xac\x8e\xa9\xa1\
+3J-h\x0a\xd01j\x18\x02e\xc2\x85\x1f`\xa1\
+\xa2\x9a\xbc\xd8\xa9\x02\xc58\x83|2E\xcbB\x8c\x8e\
+\x13\xcbb\x0a\x969\x0a\x1d\x19F\x07e_tH\x8b\
+\x87\xa1U\xfb\xe1,d\x81\xd5~*Mg-<\x17\
+T'\x1e\xf6k\x88%\x17\x7f\x13\xd8\x1f\x05V\xe5\xe1\
+\x99\xe4\xcc\x8f\xc5F\xaa\x85@\xb5\x8cj-\x0b\xc3\xe0\
+@\xb4\x07k\xc1D\x1e\x85\x0e\x8e\xb0\x91\x8f\xcb\xc7$\
+\x1a\x89\xc6D\xa0X\xfb\xa7\x13\xa6\x02\x16\x064\x06!\
+\xc9c\xfa\x18\xa6\x0a\x8d\x14f`\xa2\xda\xafh\x09\x06\
+B\xb2\xda~\x80\x0a#\x92\x0c\xaf:v\x14\x0a[\x80\
+C\x8b\x0d\x94\xa2\xca\xea\x93)\xc9(<|\x0b\x8b\x81\
+\xbd\xbd\x8a/\x1b}\x85\x05I\x0a$j\x85%sA\
+\x13Z\xb7\x07Sq\x0ac\xd9\xab[\x9aLu\xd9\xea\
+\x96\xadj\x99k\xa30\xd2A\x9f#\x8c\xfbb\xa4\x92\
+\x05\x0cY:\x9e7\x8b\xbbXg\xc0\x85&\xc5,\xab\
+\xe5T\x9a\xcd\x18eq4\xd5\xc8\xf9\x94\x1e`B\x06\
+\xc4\xe4vh\xc3`h\xe9\x8d=aQ`\xe5q8\
+\x0d\xfaf\x17\xa2]q:t\xcfr\xa0\x1e\xaf\x17\x05\
+\x941=\xd4O\xdf\x07\x93\xb2\x8c\xf0\xc1?\xf1>\x9c\
+\x94\xc7\xf7\xe1\xbb\xece\xe6\xbe\xbd\xcc\xba\xfd\x06\xa83\
+?;?\xf7m7\xce\x89\xc6\xb8\xbd\xcc8\xfe\x04\x8d\
+>8P\x8es\x16\x00\xa3\x1dm\x7f3\xc6\xf1\xe0\x80\
+c\xc1A\xc7\x83C\xf6\xdf\xe9\xeeB\xf4\xde\xf6\xe0B\
+\xb2}{x)M\xb2\xdd7\x04\xe6Y\x9e\xcdp\xe7\
+\x0d\x81\xca\x12\xc3\xcc\xcf\xce\x9bF\x8a;\x9e4\xe1B\
+\xb6?\x9c$\x8c\xef\x85\x93\x84\xf90G%\x8c\xb7\xcd\
+\x22\xa4\xe9\xe0\xf9l\xad\x8c\xba\x85\x0cl\xd3p\x95E\
+\x92\x07\xae\x8b\xf0\xbe\xbd\x96\xc2\xb9\xc6%\x800=6\
+\xb8\xcb\xf9\xacM\xdb\xee\xe7\x14\xc1\xed=\xf1\xf9E\x93\
+\xd6\xd0\x1ca\x1c\x9c9\xe4] -\xe6\xbbz\x17\x12\
+\xc0m\x0e^9kv[\xed\xd8\x1cH\x80\xc7;\xdb\
+\xe1\x18\xb9\xcf!\x8fx\x97;rD\xf0\x03n1.\
+\xa1.\xcd\xda\xf8\xe6\xd0+\x06\x85\xb1\x1f\xa7\x1c\xc6\xe9\
+\x90\xa3\xcb\xf5\xb1\xeb\xfa\xf8\xb6o\x0c\xdc\x1d'\x1f\x18\
+#f\x84^\x8c\x92\x861\x1a;/\xb1>\xcf\xb1>\
+\x19\xb2>cR\xa7Sv\xd8\xae\x1a\xb1\xcfAq\xb9\
+\xc3>\x7f\xc3\xb88\xa7\xad\x0f\x8a\xcb\x1d6\xcb\xcf\xe4\
+\x0c\xe6\xf2\x0e\xeb\xf7y\x9a\x9e\x93F\xd7\xcf\xedd!\
+\xdd\xe1L\xf0\xa1\xf3\xc5\x87\xcf\x22\x1d>\xe1|\xf88\
+\xd4\x83/*\xce\xec3\x7f\x07\x19m\xfe\xd3\x87\xa5?\
+\x13$\x99\xac\x98;\x14\xa0\xd0\xac\x13\x09P\x10\x03\xba\
+\x93<Q\xc0X\xc1\x1f\xad\xbd\xd2\xec\xf9^\xd5\x9d\x0e\
+\xf1^\x15\x8a\x02\x18\x93lx\xb1\xea.\x1f\xec\xc5*\
+\x19\xc8A\x160\x15h\xffh\x22\x1f\xd9:&\x03;\
+\xf6:&\x9d\xe8\xc7\xba\x8c\xc9 ~\x12\xcb\x98\x0c\xe1\
+\x13^\xc6dH\x9f\xec2&C\xf9\xd0\x1b\xcbZ\x0a\
+\xb3B$\xa2\xd5\xf6\x94\x0aa\x88U\xf34.\xc8\xa1\
+\xa8\xb5kH+b!6\xec\xb0\x90\xa3x\xb8\xc4d\
+\x0aK6\x9e#\xa6\xe7Ilx\x01\x82\xc1\xfe\xd3\xae\
+V\x84\x5c\xad\x86\xb9h\xc51\xc4k\xd0\x07\x85\x0d%\
+H\xadNM\xfb\x12\x8f\xf87\x00\x22\xc7\x04$t\x81\
+\x87\xe6\x13 \x92\x0b\xfbp\xaa\x16h\x0fz\xd4\xf1\x83\
+\x97\xb4\x96f;8t\x02\xa3\x10\xa0a\x04\x80\xf6E\
+\xc0\xb4\x88\x04\x22U\x93p.\x19\xe0X\x11 .\xae\
+aR[\x88gA'\x921z\xc1\xb0^\xa9\xff&\
+\xbd\x86k6}\x5c\xb5r\x8cV\xbf'\xeaiB\x03\
+\xb5z(\xa4\xd53\xab\xbc\x06\x13n5\xc6p\xdar\
+\xf5<rpR.V\xcfCQ\xf4P\xdf\x05\x83\xd5\
+\xb22\xb1\xf5u%\xf2\xf5x\xcd\x91\xb0\x88\x10r\x05\
+ \xa2D\xa0\xf3G\x06\xc3\xa0\x8f33\xdb\xfb\xdd\xb5\
+\x17\xf2$\xae-\x81\xc8\x8au\x06>\xca\xc5CP\xb9\
+\xbep#.L\xfe\xd0z\xee\xde\xdc\xceg\xe7\x7f\x99\
+\xdf\xbf\xbeiK?\xbfh%o\x85\x15\x940\x041\
+n\x91\x8a\x82\x86\x1c\xcde\x116\x0c\xf3Q\xed\x87\x8e\
+\xdaSZq\x05\xeft\x0c\x1e%9\x99+l(\x85\
+\x5c#\xaa\xfbp)\xae\xfd\x90\x8d\xd0\x96\xf2\x88\xde\xc2\
+\xa0\x1e\xd8h\x85E\x14\xea^V\xa7\x02\x04\x81\xb5\xcd\
+\xac\xd2\x9cui\x86\xc6\xe2P4\xd0(t\x82\xc5\xcd\
+\xdd\xbc\x9d\xe0\xa4b\x19F\x92\xed\x0f(\x11\x1e\x19M\
+6k\x01\x0f\xb4\x89\x96\x14LH\x19\xd3\xca\xc8\x07!\
+\x14\xc9\x9f[Y7l\x0f\xb2\x91\xc1\xc7\xf8V\xa6\x85\
+\xd9T:\xad\x8c\x8b\x0b\x19\xc7qZ\x1ao\xe3W\xc6\
+\xb7\xb4\xb0\x12di\xb5\xa5M\xd1\x8aYH\xd43V\
+W\x89\xceV\x88\x86\xdb\x1ai\x98\x81\xb7\x91\xb8\x12\x92\
+\xb8\xb4\x97A.,2\x81\x22\x08\x06\x88\x13\x06*\xca\
+):Q(\xa0\xaa\xa9\xa3\x1a\x1a\x1e\xb5\xa1\x15TM\
+\x8dN`\x07RHW\xa2\xba\x04\x0b\xee\x8c\xe7\xa4\xe8\
+\x10\x5c\xef6%v=PO\x85\x83\xbe\xdb\x04\x0e\xb4\
+X\x842\x07\x97\x03\x9c{\x9bv}a\x97I\x07z\
+\x9b\x8b\xeb\xacG,\x07`b\x1f\xd4X\xe7\xb6\xf3\x02\
+\x08\x0a<\xday\xf7$\xb8\xbf\xc6f.C\x1a\x03\xa0\
+I\xec\xa8\xb1\x99\xdb\xb0\xc6\x88\xb3\xdcYc3\x8f1\
+\x1a\x9bU\x8dW\xd8Z\xecp\xcc\xfa\xc5\x19^\xd0\xee\
+k\x144|\xfer\xf73\xa0\x19\x83+\xf3w\xe1\x80\
+\x1dNi>\xa3\x99\xaeU\xe6\x188e\x9boE\x9c\
+\x9f\x9f\xedl@\xb2U\x939\xbfx\xb9\xbb\x01\x89\x8f\
+\xd1\x98qHc\x07v\x80\x9d5f\x1e\xd6\xf8\xe5\x85\
+\xce\x01v\xd6\x98u\x84\xc6\x22v\x94ua&\x12G\
+r\x8b&\x0ac4\x86a\xb7\x188\xe7\xf9\xce\x1a\x83\
+\x0dk\xcc\xe1\xe7ko\x87\x875\x868\xf6\xe9\xd9f\
+f}\x072\xd8A\xd6\x17p\x9a\x92\xf2\xc4J\x06\xa3\
+(\xb6\x1f5\x99z\xf1@Ik\x7f`Q5\x0cn\
+C*\xa2\x12d\x88\x93\xa9e\xb1\x0cC{\xfe\x9eH\
+\xfe\xcd\xec\xbc{\xbe\xb6\x03\xb0v\xde\x85\x01\x17\x0d\xa8\
+S;\xab$\x17\xef\x92\xdc.(TSb\xc4{\xa6\
+\xfe\xe0\xad\xcf$\x11\xea\xca\x9c:\xa5\x1cQG\x89R\
+7>n3\x9dz\xf0\xc2\x8b\xf5\x16^\xa9\x0eU\xb1\
+@J\xe6Ci\x979\x08\x98)5\x0e\xd7\x82\x91\xc8\
+\xdc]2\x03\xe0\xa1\xdb\xe8\xd5?\x96\x1b\xde\x1e\xedE\
+\xc3!-Z\xab\xe3\x00\x13\xa5\xe7\x1f\x12);\xc8\xdb\
+o(\x82\x94\xc04\x81b\x98\xac\x12\xcf\xfbq\xd9\xb9\
+n\xc8\x22b\xa5n\x1c \xf3C\xe2\xe4\xf0\xb1\xe3\xc4\
+%\x0dA*LQ\x885\xc5?(L\xf43L\x9d\
+\xc0\xad7\xa6\xab\xaf\xe4T\x0b\xb7\xba\xd6\xed\xed\xf5\xd5\
+\xcf\xda\xdd\xe5:\xb6DF\xf1\x0f\xefqM\xb8W#\
+\xa9{^\x8c\x0ap8Rw&,\x91\x1d\x1f\xd4Z\
+#i\x15\x85\xe2\x14\x19z\x10\xfb\x99\xf0\xc1\x9d\xa8\xb9\
+\xf4*\x1f\xd0\x99\xb1)\x90\xe6\xaeu\xf6\x14\x8a\x06\x98\
+x\xb5\xc1,\xc9\xc2\x5c\xd7\x91D\x14c\x96\xf8\xa8\xba\
+Q\x0a\xdc\xe5p6\xeb,\xbf$+\xc9f\xab\xcb/\
+E\xdc\x8d\xaa\xceT\x802T>\x9cFL\x8c\x87\xf1\
+Od\xc4F9\xc1\xba,O\xb3\x10i\x80w\x0fH\
+Mg\xc2Z\xf3\x95\xe6\x11\x05\xb1B\x14\xa1\xf9AQ\
+\xe0\x03\xa1\xc0L\xd4\xa2P\x12\x15R:h jq\
+3\x8f\x8a\x06\x16\x08I\xa8\x11$c\xe1\x0e\x0d-=\
+8#\xe9\x81\xc1`b\xef\x05C\xb8\x9e/hPR\
+\x94\x14\xab\xeb\x02-\xc1\xa0\xb5\x07i\x88\xaa\x0d\x0f\xe3\
+\xf8\xe1\xfc\x10y\xc0.\xed\x15\x8a\x00\x81UOeR\
+\xc0\x0d\xa5\xbe\xd4\xa845\xd4\x87\x92F\x84r\xe0\xe3\
+\xf7(\x13zO\xf1\x87<\xc0j\xda\x82 \x10\xa1\x13\
+(A\x01f\x8b\xf7\x0bf\xe2\x92\xd6^F\x183\xf9\
+\x04\x13\x8b\x88\x11O\x8c\x0a)'\xd8P\xf8\xd1\x8e\xf5\
+\x8c\x0b\x08\xbak}G/%=\x05\xa2\xbe\x93\xe4\xa2\
+\x8a\x84\xf5]\xbfb\xd1\xa4\xcc\xfa\xa19.\xe2\xe2F\
+\x0b\x80\xcf\xae.\xdf\xfcuv\xff\xba\xbd\xf9x\xbdy\
+\x12\xff\x91\x8a\x91\xda\x1d0o\x9a\xcb\xee\xd0\xbf]\xcf\
+{\xf5\xe2\xed\xed\xd5\xaf\x7f\xb5\x8a2#\xf3\xf3\xdf\xb4\
+w;\xb3\x00w\xf7\xb77\xdf\xcc_\x5c\xdf\x5c\xcf\x1f\
+\xae\xa7\x8bO\x12\xbc@+\x92\x0a\x00!\x8f7\xda\xfa\
+lj\xfe\xc5\xed\xcd\xdb\xeb\xf3n\xe6\x7f\xdf\x5c^\xaf\
+\xe6~{\xd9\xac\xeb\xbd\xbal\xfe\xf7\xe2\xe9\xf1\xf3\xd9\
+\xdd\xeb\xd9\xed\xed\xec\xc7\x07i5w9\x07\xf1B\x0a\
+\xeaC\xf6\xda\x17\xa1\x1a\xbd\xffr2\xc5\x90\xe2l\xa2\
+\x13\xf2\x92\xc2\xae'\x9f7\xb9)%]\xdd\x9er\xf1\
+dJ\xa0\x85@\xcd'\x8c%0\xd0\xad\xc9D*\xe9\
+@6\xe1,\x1a\xe4F-\x03\x22(\xe9.1\x11/\
+\x1a\x99\xc9m\xa6\x94\xd0\xd4\x89F!\x22\xc3\x87<\x83\
+$\x9fX\x14\xf7\x14\xc8\x93\xaf\xba\xd9i%\x88@\xb0\
+e[\xb3\x11\xa0\x84\xb3G\x9b\x89\x85U\xbc\xcd\x94\x82\
+\x14\xd4f\xa2\x17\x86&\xd5\xcc\x96/*\x15\xb4\x8c.\
+m\xab\xafDA\x0c\xe4.\x0b\x14)al\x5c\x85u\
+3k\xc1\xbe\xeafW5>_f\x1b\xb2<iL\
+KY\x92*\xd8\x85\x06\xd5\x8a\x02\x99\xac\xc2\x88&\x85\
+\xcc\x89\x9e \xc7&\xd3\xa5H:\xf2c\xe5H\xbe\xb7\
+\x1e\xffu\xb2R\xbf\xc2E\x02\x01e\xc1\xd8\xa3p:\
+\xe4S\xb6\xb6\x99T@\x02}\x22Z\x12\xbdek^\
+\xd0#p\xa2P<\x9c\xd4\x96\xe5\xa2\xc2\xe6\x9c\x13\xd5\
+\xa2\x19\x16\xad\x0aY\x92\xb2!5,\xc1\xccA\xcb\xcc\
+\xf6\xa9E\xf5B\x02\xd0\x02.\x82,\xea\x04\xd8\xc9\xfe\
+\xfc!\x1b\xc5\xed\x89\x05\xb6\x99^\x10T\x1fEy\xb6\
+yT(\x05\xe2\x9db\xa5\x17!qzP R\xdb\
+L,\xe0\x1aO\x9a\xf2\xfbQ\xf9Wg\xba\xae\xf5\x00\
+\x8c\xcb\xc8\xa7\xbbI\xe2\xfa\xe6|\xden\x94\xb8k|\
+\xc6\xdd\xd9\xe2\xef\xee\xe1\xbf\xe5\xcc\xde\xa3'9\xba\x9b\
+\xaf\x0eh\xbc\x9b\x87\x22i\x08j\x13(\x22\x90A\xd9\
+\xfavg\x03Mi/\x97\xce\xdf&\xd3\xcc\x82\x82\xe2\
+\x93XDj\xa0\xb1\x8d\x9bw/\xca\xcc\x8e\xef\xbay\
+\xac\xeb\xd5\x22\x0a\x08\x05\xe4\x87p\xf3\xac\xd5\xcdoY\
+\xbf\xeb\xd6\xe1+\x8e4\xbd0a\xe0\xaa\x1f\x05/j\
+\x88\xd4\xf5\xa3\x88^<\x11\xb8\xebG\x91\xb4\xa8\xaa\xd1\
+;\x0e\x80\xb9\xa8Q\xc8\x8a\xb3`/\x22\x12V\x1d\xe9\
+2\x13S\x8cW\x1ci\xcd\xee:\xd2\x9a\xbd\xea\xdb\x98\
+K\x0a\xbc\xe3\x05)KDp\xae:R\x93\xe2\xaa\xc4\
+\xba\xeaH\x0d\x8a\x03Kj\x97\x85\xb6\xc5\x8dL\xad\xc2\
+\xba\x99]GZ\xb3W\x1d\xa9\xb6\xc5\x0d7\xedzR\
+\xc3\x02\x01\xee\xda\x05\xc7\xb2@\x04\x00\xae\x02\xe9VB\
+I\x03\xbb\x9e4\xbc@\x90\xa4WOZ+r\xc5\x91\
+\xd6\xecU?\x9aP2H4\xba~4\xa4\x18\xb3\xb2\
+V?\xeaY\xdc\x04\xddV\x1d\x96KI%\xd7\x8e\x1b\
+u*\x8a\x8a\x9e\xd5\x8d.2E=YV\xdd(\x12\
+\x15Tg\xef\xba\xd1\x87l\x00\xa4\xae\x1bE\xccb\x09\
+a]?\x8a(%<E\xde\xf1\xa3\x08\x05\x10 V\
+\xfc(p1C\xb4\xeaH+*+~t\xdb\xd8)\
+?\xbd\xd8\xe9\xdf\xe5\xe5\xf3\x83zy\xc4\xe2d\xc1\xfa\
+\x11\xb8\xf9\x8fk%;\x11\xd9\xff\xf7K\xb9\x8d\xf2\x90\
+K\xb9\xc7o\xb4Kz\xdce\xe7\xa4\x13d.\x00@\
+\x13\x82\x12n\xd4j\xbaDW\x0bv\xbei>\x85B\
+(Q_<\xc6\x93\xc2\x8f\xca\xf4|\xee?\xa8\x7f\xfd\
+\x86\xd3\x17\xc8_\xac\xbde\xee\xfbf}H?+\xd6\
+\xdf\x7f\x8e\x9f\xbf\xcb\xaa\xb0*\xf7\xf3\xb3~~\xf0e\
+*\xc4\x1a?\xc3\x94~~\xb1\x81\x1f\xc4\xe7\x00k\xfc\
+\x5c\xbd\x9f_\xc2&~\xfe\xfb5~\x0f.\xb7\x1a\xcd\
+.\xab\xc5\x80zv\x99\x83\xecp\x98\xd2\xf9|>t\
+\xdaI\x97\xb5\xedp\x84\x07\xcdc~\xbe\xb6rb\xc4\
+\xfa8\x83}6\xdd\x1b\x1d\xf6\xf0\x97.k\x19q\xb6\
+\xd4h8R{4N\xdf^c\xe3y\xee\xa0q\xe6\
+\xbfQc\x91\xdc\xc3\x00Dq{8`\xf1\xb7\xfd\xe1\
+b\xdc\x03\xc7\x00\xeb\x0a\xc7\xa8c\x17\xc8Y6\x1f\xbb\
+`\xd2\x9e\xba@\xc5\xda?\xef;{\x01)\x9a[&\
+\xfb\x1e\xbd\x00\xf5\xe9\xbd\xce]\xa8\xdb\x18\xa1 Ag\
+[\xa7qAM`]y\x87\xeb\x085\xf2\xa94\x83\
+\xeb{@\xb9\xfd{\xecP\x093E'\x88\xc5m\x22\
+%\x93\x84l\xcc\xb1\xc9\x1c\xc3{y|\xff\x03N%\
+\x0a!\xa1b}e\xa2E-\xba\xf0X\x96$\x86\x88\
+\x95]\x06\x9cd\x1f\x03<0\x1a\x9e\x9f\xb0\xf1x\x0c\
+\xa2\xc3\x86{\x1b\x8f\x14\x0a\xef\xc2\x93\xd8\x96\xda\xc1\xba\
+K\x0d\xea7\xf5\xdb\xfb\x1eJ\xb2\xc5fi\x82\x09\x8c\
+R\xdd\x06U\x17\x80\x9f\xa6\xea2B\xf5\xc11W\xdd\
+\xa6\x93X\x02\x14:\xc3U\xf5\x02\x1a\x8aR\x07`Y\
+@5 \x9f\xc6\x0d\xca\xc5\x18hx4V\x143%\
+'^X1\x89s>\x8d\xc94\x0a\x18\x93\xb0\xb6\xbf\
+\xb0\x90;!\xcadJP\x88\xc4\xc9\x9aK.\x99f\
+\x9c\xfb\x0f\xda\x88\x9d\x86\x9b\x8e\xc4\x9e rQ\x0f$\
+\xad{\x0b\xa5(#i\x1d\xc4b\x97\xe4\xec]\x92\xb3\
+.\xc9\x00\x90V\x14<\xc3\xf2ax\xab\x08\x89>\x99\
+\x1a\x97\x16<\xc3\xc9\x94\xa9\xa4\x03\xa2\x8eB\xec\xa8\xae\
+8\xad\xb01hg\xe5R\x14\x03\xc3\xce6B\xa6\x02\
+\x8aa\xb5\xd5\x09\x17\xcf\x04\xb2=}\xb1\x17\x1533\
+?\x8c?\xb6m6\xb6\xcb\xdeF\x85j\x1d\x9bB+\
+\x10VM\xca\xb1\xb8+Q\xdd\xad\xda\xa58\xebR\x0c\
+O\x22\x98B\xa04H\x91a*\xd0d\xda\x82\x06\x98\
+!\x13.\x9c@\x086A/\x0c\x09\xd6\xdcV/\x96\
+L\xe3\x1a\xa5\x0d;u\x8b\xdc\xdb\xa9\xb7pdp5\
+\xb1\xb4\xc2\xceH\x5c\x9d:A\x11\xf2\x1a\x0cU\x9aA\
+\x13CwR\x89\x87f\x08\xee\x84\xe1\x13+\xaaJ)\
+6\x99JIAs\xc0Q66\xec\xfd\x15\xf1\xa7c\
+c\xcc\x07\xb61\x1a\xb61\xdb\xdb\xc6(J\x0a%\xd5\
+5Jb\x85HS\xde\x99\x89\xe5\xf0\xea\xc6\x1c\x8b\x91\
+\x09\xc8\x9e6\x86%\xc4\xc8\x95\x0fec0lc\xc0\
+?w\x8e\x151\xe1m:\xc7\x9f=\x7f/~8\xd6\
+\xf3\xff\xf4[%\xc76\xad\xf2g\xcf\xdfgc\xac\xc7\
+\xf0\xfc\x87\x8c\x0a\x0e\x14\xa9\x8c\xb11\x1a\x19]\xfc\xf4\
+\x83|\xda*\xc8\xffy\xf8\xbd\xb1-\x92\x8c\x18~\x0f\
+\xb4\xc5\xe1\xa9\x97\xf1\xb3<c\xec\x07GL\xdf|\xda\
+\xaac\x1cU\xf5\xf1\xd3\xb0\xe3'\x85\xc7\xa0\xa3\xa3f\
+s\x8f\x8f\xce\xf87\x0a\xa3\xe0\x19\xf1\xa6\xe4\xa7o<\
+\x90\xc7@\xe7\xdf\xe9.8$\x8e9\xc7\xefZ\x9c\xc2\
+\xb9\xb3\xad\x0e\xa28\x88p\xd5\xc9\xaaBT\x1c\x00M\
+\xc7(d\xc3\x0a%\xef\xab\x10e\xd1`\xd4\xce\x89\xc7\
+Y \x08\xbdV\x92[1[=!\x0a\xad\xa0\x90\xd1\
+\x18\xbd\xb6\xe8\xcd\xc3\xf6\xd5+l\x11\xe8zV\xe3\x83\
+\x22\x18\xa8T]NafJ\xc4\x07\xbd*\xcd(\xbd\
+h\x84G\xfeX\x1b\x15|\x98\xb7G\xc1E\x11C\xad\
+\xbbu\x89\x04\x11\xb3\x86\xafV\xc8\x05\xb53\x88\x84\xa2\
+\x16.8|\xf2\x06QF\xda\xc3\xb8\x8a\x1c\xc0\x01'\
+S\xa2\x92\x14\x99\xb6\xb8DO\x8c\x18\xb1\xb8/8~\
+~O\xb4#bv\xc4\xa90)\x9a\xec\x150\x91\x82\
+\x99a^\xad\x8c:$gk$g\x1d\x92\xe1/\xec\
+\x98\x01\xa9L\xa0hcNE\x83\x04\xd4'R\xc4)\
+\xd4c\x82$\x85\xcd\xbd\xc1\x0e#\x0b\xa59\x8f\xc2N\
+\x86\xb1c\xde\x13;/i\xd1\xc1\x8e\xa3\x88\xb0q\x17\
+\xbajh\xf5v\x076\xd2\xe1v)\xc8\xd1\x02\x02\x05\
+\x0d\xd84\x1b\xf8\x048\x09y\xc2\x85=\x0c\xad\x9d\x12\
+\x83\x82\xc9\x82\xb1\xf8\x96\x13\xa2j\x8e3;\x1a\xf6k\
+d\x1f\xd7\xccE\xf7 \x17\x19u\xa6v0l=+\
+1\xder\x22\x8b\xa2{R\xd7tB!\xb2.v_\
+\xa59\xeb\xd2\x0c\xda\x10e\xaa\xb0\xb66\xe4\x0a\x94\x18\
+\x13,\xa0\xee\xc4\xd6\x5c\x05\x13p\xdb\xe4p\xe1\xd3 \
+'S\x8a\x22\xe4\x068\xc6\x84h\x8bx\x93\xe3g\xcf\
+\xf5^\xecl\x1b\xcf\xf5\xf3\x0b\x90^\xfcd\x9b\x17 \
+?\xe3\xd7\x8b\x1f}\xc4K\x07F\xf8u\x82m&\xe4\
+\x7f\xb6\x8b>\xbb\xc0\x18\xf1\xd2\xe7\x13\xc4O\x0f\x8d\x9f\
+}\xc4\xcb%\x8e\x1fV\xa1\x1c\xe6\x9dk\xfd\x5c^\xf7\
+\x8bx\x0fN\xeb\xf5\xfc\xf2\xd5\xeb\x86\x1e\x8bf\x90\xe9\
+\x12\xce\x87=Y\x99\xf8\xb4\x85n\xb1\xfb\xb3\xa5K\xe0\
+d\xc0e\xd9\x16t\xa2\x98\xa6\xe0\x1b\x8b\xb2\xf6\xfd\xbd\
+\xfe/\xe8\x91\xbc\xf7\xeb{XX\xdc\xd4\x16\x05\xdf\xa8\
+t\xfd\xfc^\x8f\xf6\x16\xf8\x8e\xf6\xa8\x9eB]\xed\xc1\
+#\x1d\xc5pU}\x12\xf1\xa0\xae\xfaH\xc4\x89\x07S\
+\xdf\x82{>>\xc8\x84\x892^{e>PY\x95\
+\xb5\xa7\xaa\xc0\x00Xu|a9\xed@\x85\xe5\x8c\x1e\
+`%w+\xe8\xaa\xa3\x1a\xf2\x9e\x18l\xb8\x9c,\x9a\
+b\x11\xa34\x9fO\x97>\xd4h1\x0f\xe2\x854\xb6\
+\x0d\x82\xeb\xd1\x07D\xa9_\xe3\xd7O}\xc0\xd4\xc0\xa3\
+\xb0\xd6a5[\x16%\xa1\xc7\x8d\x97\xa4\x5c\x00\xa3n\
+\x0c\xae\xcf<\xf5\x0bdZ8{>O\x1e#w'\
+\xf5lNB\xd8\x7f\xa7V\xe5;{\xf1\xed\xe5\xf9_\
+\x9bM\xe7\xf7\x7fkn\xeeX\xd0a\x0eEG\xf0\x18\
+T\xeb\xcb\xc5\xdfV{,\x87{\x9e\x90\xc9\xd4\x5c\xea\
+\xcb!s\xa9\xd3\x07\x1c\xc5\x9d\xb9s\xbcY\xd6I\xf9\
+zw\xef7B\x89\xf2^\xe3\xa1D\xeb\x07\x80\xacM\
+C\xa7D\xf7\x18\x10%F?k\xc66\x0d\xb1.\x88\
+$\xbd\xfci\x83\x05\xc9y\x9b\x06\xf9\xb3\x8b\x02\x00\xf6\
+\xcb\xa0~\x19\x0am\x1a\x94!\xc4\x90\x99\xd9/C6\
+\xc8\xf06\x0d\xcb`\x8b\xcd26T\xb3I\x9b\x86e\
+H\x0e\xe8\xb1\xa1\xbe]\xdb4,\xc3@{\xf9\xf3\x86\
+\xfa\x8eY\x9b\x86\xf9;\xe2F\x1dxC}\xcf\xb8M\
+\xc32\x02e\xa3Mq\x7f}\xdf]^}7\xbf\x1d\
+\x16\x91\x88\x9bEl\xa8\xee\xf3\x8b6m!Cx3\
+T\xb1\x01\xaa\x8b&\x0d\xcbPt\x09\x8f\xe8\x95!\x1b\
+\xaa<\xa5M\xc32\x98t\xa3\x1eB\x1b\xcc\xf6\xacM\
+\xc32T137\xe8!\x1b\x9a\x9f\xb6iXF\x88\
+\x0a\x98\xf7\xcb\xd8P\xe7Jm\x1a\x94a\x10\x06\x00\xd4\
+/cC\x9d\x0b\xb7iX\x86\xa0m\xb4]\xc5~\x19\
+\x9cm\x1a\x96\x91\x14\x1aD\xfd2x\x83\x0c\x1eV\xc2\
+9}c\x85\xab\x8e\xee\xfb\x22#2\xd3\xfbe\xf8\x86\
+\x06h\xb3\x98\x0d5\x8e\x9eo\xef\x8c;\x84d8\xfa\
+\xb9]~\x92R\xc5\xac\xb3v\x91J\xa6\xad\xac]\x8c\
+\xe2NJ}\xcb\x06\xc3\xed\xfd1Mxl\xf0~\xd9\
+\xa6}c\x9a\x88M\x87ax\x9b\x06\xeb\x15$\xa5\x9f\
+?\xf5\xf3\x7f\x99m\x1a\xe4\x8f \xd9\xcf_6\xd8\xcc\
+\xbcM\xc3\xfc\x85\x09\x93\xb8_\x86m\x90\x91M\x1a\xd6\
+\x81P\xad\x9f\x7fl\xb2\xfb6\x0d\xf3\xb7\xb4M\x9dB\
+\xe4\x86z6o\xd3pl\x99\xb1\x81?\x1d\x80\x7fn\
+\x8c]#7\xc5\x95\xd2\xa4mb\xbe\xe0\xcd8m\xa8\
+k\xc16\x0d\xcbH\xd5\xcd26\x8d#\xb4M[\x04\
+\x01\x19\x91\xda\xefK\x01F\xfbkuHS\xe2~\x19\
+\xb4A\xc6\xbcM\xc32R\xa2\x9f\xff\xa6\xfa\xbeh\xd3\
+\x16\x9d\xb3n\x0e\x00`C};\xb4iXF\xf2\xe6\
+\x18\x1cb\x83\x8c\x97m\xda\xa2\x7f\xd6\xd8\xd8?#l\
+\x90q\xd1\xa6a\x19\x03>$\x91F\xda\x14\x1ek\xee\
+\xc1#\x0b\x12\x81o\x98\x810\xf7\x12\xee\x80\x87\x9c\x87\
+\x08\xa3\x9e>\xdbd\xf4`\x1e`C\xa7a6\xda\xe1\
+\x22K\xf4\xf3\x8f\x0d\x1d7\xb5i\x8b\x81#\xf7;s\
+\x87\x0d\x9d\xdelx\xe4k\x94\x1b[D8m\x10\x80\
+m\x1a\x8eX\x95bc\x8f\xe4\x1b*9\xcf\xdb\xf4\x11\
+G\xacV\x80X\xea\xd2;\xc5\x82\xc0J\xefD\xac\xcc\
+\xd1\x1b\xb1\xaa\xf4X\xbf\xda\x86\xb0\xf2e\x9b\xf6\x8eX\
+u\x83aR\xb4i\x9b\xde\x937\xf5\x0aa0\xa2^\
+G\x9fd\x97\xb2\xcf\xb9ni\xdbO\x9c\xf3\xffk\xef\
+\xca\x9a\xdb8\x92\xf4\xf3\xfaW0\xfc\xa6X\xaa\x94\xf7\
+\xa1\xf1:B\xa6\xad\x89\x89\xf0\xecN\xec<\xec\xa3\x83\
+\x92@\x8df$\xd3AQ\xbe~\xfdVv\x03l4\
+\x89&\x05\x8a\x1c\xcb\x07a\x19\xa8\xaf\xd1\xd9\x95_\xe5\
+U\x05\xa0\xcb\x8f\x85\x9e\xbf\xff]\xce\xe2\xfd\xd7\xce\x9f\
+\x9d\x9c\xe8\xd55\xf9\xdb\xdf\xe4\x8c%n\xc1\x06+\xec\
+q\xd3\xbf\xdeg\xdc\xd1\xe5\x05\xc9\xb4\xcf=\xce\xe4\xc5\
+\x9d\x92ax\x1b2\x8c\xdf\x9f\x8c\xd5\x8b\x93\xda\xc4\xf3\
+=M\x83M\x17\xd8X\x12}\x977\xc0\xe3\xb0\xdb\xd0\
+\x11q_w@\xe4\x84_\xf2~\x80\x00\xb7\xa0C\x80\
+\xf6\xb0\x0e=9\xd9\xe3~\x80 \x0bt,\x8a\xbeK\
+:\xc8nC\x07\xeda\x1d\xe5\xdf{\xd0\xc1\xb0_\xe8\
+\xb8[:8nC\x87\xc0>t\x10\xeeA\x87\xec\x15\
+I\x09\xef\x96\x8e\xe4\xdb\xd0\x91z_\xb1C\xd2\xffM\
+\xb1cY\xe7\x05\xae\x14\xf8\xe6}\xc9\xf5\x04/\xcf\xf7\
+o\xd8\x7f\x97\x15\xdecWi\xe1\x14~~\xbdd\xbc\
+*\xd9?\xc06\x14o\x93f\x15y/W9\x86\x17\
+\xefk\x1b\x8a\x0bivY\xf4\x1d\xba\x8aY\xde\x82\x0e\
+s\xbc/W1\xe7\x8f\xd5U,\xf2\xc6\xdd\xb9\xe1\xe4\
+$`OW\xb1\xac\xba\xf1\x16\x92\xe1&W\xb1\xe4\x0f\
+\xb0\x0d\xc7\xdb\xd4\x1c\x8et_\xb6\xe1(\xbf`\x09\xe6\
+\xcc\xb7\xa1\x83\xf5\xbeJ0g\xdf\xaf\x04\xbb\xe3[2\
+3`\xfe*o\xc9|\xfb\xfd>\xa6\x1d\x12\xfc\x9b\xc5\
+m\x15\xfcNvr}\x88\xd9\xb2\xfe\xea\xe6&\x0f\xa6\
+\x1fM\xc2\xd6\x86\xeaH\x8dA\xfcb\xdd\x87\xb3\xa9\x86\
+\xde\x91\x8aq\x8d\x8a\xfe\xe9]l\xf8\x10N,\x9b;\
+\x14\x04\xf6\x07\xd5\xafz\x9aqP\xfd\x10\xd6Z\xb0\xd5\
+c?\xfd\xe7;\x8a\xde\x9e\x80\xfcf1\x15x\xee\xb1\
+\xdfdJc\x99\x96\xabK\x87\x0elm\x8b\x1b\xd6\x94\
+u\xda\x147\xa5\xb7\xcd\xeeH\x0d\x84e=\x02\xf7\xd1\
+C\xb7T\xe0Y\xef\xc3t\xd6\xfbj\xdfQ\xef\xf1\x9a\
+\xde\xf3\x1d\xf4\xde\x1b%O\x9d\xd7j\xdeU\xdf\xe9\x9a\
+\xbe\xeb>}\xb7\xad\xbe\xcb\xd4\xf7h\x00@S\xe7m\
+h\x8fQ\xfd\xc5\xea\xe4\xed\xe7\x9fM{\xa2\x1d\xbfY\
+\xbd\xf8\xfe\xd5\xea\x87\x99\x22?\xbc\xfa\xf6\xc5\xe9\x0f\x0f\
+7_e\xb7\xf0Ow\x1d\xdf|\x81\x1d\x88.E\xfc\
+\xe3\x97\xab\xbe\x01S\x7f\xcf\x7f}\xba\xe3\xd0:\xd3\x0c\
+\x9fx\x8c\xea\xbe{\xd5\xb7f;}\xbd\xea\xf1\xe0\xf9\
+\xaa$n\x0e\x9c\xf5K\xec\xc0O\x9f\xfd\xb3\x13\xbd\xeb\
+\xc8\xb3\xd3\xb3\x17\xab\xb3\x8b+\xe0\x0c\x1e\xb2^\x1f\x07\
+\xab\xbf\x91\xbc\xea\xcf\x06\x1e\x8b\x83\x8b\xb1yv\xdc\xb9\
+\x9eu\xfe\xe7\xd3\xd3\x1e\xaa\xa8\xa9\x12!\xeb\xfc\xe0\xf8\
+=\xe7\xa6$pe=\xb7V\xdb\xa9\xa9G\x04\xed\xa4\
+\xb2b\x98\xc1\xceC?\x95X\xbf$\xf0\xdd\xd9Y7\
+\x8f\x87\xaf\x8f\x7fZ\xf5\x9e\x7f]O\xdf\xac\x13\xdd?\
+N\x7f(\xda*k\xbc[MPQ\xfcv\x02'Y\
+\xc3\x91\x87\xcf\x9e\x9d\xfe\xb8>\xba\x95\xeaK\xd0T_\
+\xfc\xb9\xb7\x9e\x9e\x9d\xbe\xf9\xdb\xd9\x0a\xc4\xfe\xbe:?\
+\x7f\xf5\xed\xcb2\xf3\xfa\x1b\xf3\xea\x8f?\xd5)kh\
+L\xf8\x95\x8b\xbf\xfbq\x06\xfd\xb4\x0d\xbd\xedc\xd5\xe5\
+\x14{\xcd\xcc\xfc\xf2\x81\x9f\xae\x1c\xd8\x8c\x17\x00\xf4\xfa\
+w\x0d\xae\xde|\xb7\x13\xdf26\x9a\xde\xba\x85\xca\x84\
+\xae\xaf\xd8/\xb8\xc1\xbe=~\xf6z5qY\x7f\xdf\
+\xbfz\xfb\xaa\x83#6:\xd5Uw\xfa\xfc\x93\xcf\xde\
+\xac\xce\x8f_\x1c\x9f\x1fo\xcci\xd3\xee3\xebO\xfe\
+\xe3\xb3o\xdf>~{\xf2C\x7f5\xbe|\xfd\xea\xf9\
+\xeam\xb5\xa6\xe6\xe0\xbe\x9b\xc2\xb0H\x9c\xff\xbc\x84b\
+\xfa\x11\xc9\xd4\x1c\xce\xfc\xcb\x97\x1d`4\x17&\x96\xea\
+b]\xe6\xd1t\x9d\xed\xab\xfe\xfd\xf4\xdd\xd9\xf3\xd5\x17\
+\xb5\xa3\xda\xdb[\x5c\xb2\x5c\xab;\xe2\x9b\xafW'\xe7\
+\xff3\x0c\xeeD\xcc\xfa2\xa7\xdf\x9d\xf7\xdd\xd9~\x1e\
+~\x1a\xb16\x9aI\xd5\xf3\xe3\xb3\x97\xab\xf3\x0d\x5c\xf2\
+\xe6H\xe92v\xa3\xffRb\xf5\xb4\xd7\x0a\xc7\xbd+\
+\x7f\xfb\xef?\x93\x8c\x8dbs-k\x0b\xad\x13\xaaj\
+\xea#\xda\x1d\xa5\xfb\xdf4\x82\xe3/.jPO\x8e\
+_\xaf=\xbc\x9fq\xbe:Z\x9b\xcf\xd3\xe1o\xc0\xbf\
+=\xfd\xebtdz\x7fw\x9e\xdas\xee\xf8\xf9$e\
+\xe8E\xb1<\xefG\xc1\x05N\x8a\xdeB\x7f\xb44g\
+%\xc5\xdf\x22\x11\x05/[I\x1d\x1b=\xe5\xb3\xb3\x17\
+'\x8f\xff\xf7\xcb\xa7}s\xd0\xe7\x8f\xff\xef\xf4\xec_\
+\xa3;\x16z\xfc\xec\xf4]'\xa3G\xae\x17\xcf\x1f\x9f\
+\x8c\xd7{\xf5\xa6G\xf6Go\xbf\x7f\xf9\x9f?\xbey\
+\xfd\xd9\xa3\xe9@\xbd\xa7\x82U\x9d\xbe\x11p\xb6z[\
+n\xd0\x9d\xfa\x1f\xe7\xe7\xdf=~\xf4\xe8\xbb\xbe\xa9a\
+;={\xd9\xcf\xeb\xff\xbdyU'<\xfa\xfby\xdf\
+\xd0\xf0/%wt\xfbu?\xfa\xab\x8b\xae=\xdax\
+\xf9\xe7\x9f|\xb2\xab\xd8\xee4M3\x85=\x8a\xd7\x9b\
+\xab\x82\x22~\x9c\x0f\xceg\x95\x8b\xabw\xec\xb5\x91\xd9\
+4s\xc4\xaa,w\x0ai\x80\x8aK\x920\x8c#\xe7\
+\x92bY\x92j,IR\x8d\xa3'1\x93D\xb0,\
+)M\x96$E>yz\xc4sI\xb4(\x09Y}\
+I\xd2\x17|t\xf4\xe5\xa5>\xc9\xb2$\xe7EI_\
+\xcaW\xf4U\xce%-3N\x90\xb9$\xe9\xab\xa3\xa7\
+\xf4T\xe7\x92bY\x92\xb0.Iz\xdaiz:\xb7\
+\x02\x86eIN\xb4(i\xf8\x9bK\x1a\x19\xbf\xfc\x99\
+\xf8M\x8e\x11\xfb9F\xde\xb5g(\xab\xaa\xcf5\xf1\
+\x91\x93\xddc\x8el\x86\xa2\xcb\xb6\x18O\xe2h&O\
+p#oa\xec)\xcd\x22\x96\xc7\xbf\x1es\x89\xbe<\
+jl\xbc<\xfe\xf5\x98K\xca\xbb\x1a\x7f\xc5EI\xca\
+\xb8O\x9f\x94\x17%\x85\xc8\xb2\x9f|q\x85'\xd5\x05\
+I\xb8(\x04\xbb\xdb\xf2\x5c\x88/\x19\xf6\xc2\x92\xe1\xb4\
+np\xff\x8b\x03;i\xdac\xd4\x8c\xf6e\xa8\xdcE\
+y.D\xd6\x0c\xcd\xf8\xb8\x91!\x84o\xeem\xdd\xe1\
+\xc3\x89\xc9; \xc6\xf1v\xc4\xe07\xf7\xb4\xa4\xf1\xc1\
+\xb4\xb8\xedK\x0b'\x7f\xc11\x17\x12\xb7\xa3\x85\xbe\xb9\
+\xb7\xd5\x92\xfd\x88\x89\x88'1O\xc9\xb1\x1c\xb4\x00\x9c\
+\x16%\x1d\xc5W\x91sI\xba,\xc9|1\x90>\xf9\
+\xe2\xc9\xd1\x93K\x92\xfc\x9a\x22H|I\xd2\x91\x1e\xd9\
+\x91\xcc%]\x93&\x10u\xb1\x08\xfa\xa2?\xe6\xc1=\
+\x97\xd3\x04\x03\xd2rp\xaf\xc7\x5c\xd22\xe3\x82\xd7\xa4\
+.\xef\x0f\x9bKZf\x5c\xcdsQ\xd2\x97\xf5\x98K\
+Zb\x1c\xf7\xf0\xb1\xcc\x05\xf7\xf8\xec\xe5\xe6m/\xd9\
+\xd6\xa1\xe6|\xe7\xdd\xb2\xb31\xb3\x1f\x22\xd7\xb3\x02?\
+\xe8s\xa5\xfa\xb9\xff8\x09\x9a\xd6/N_\xacj\xaa\
+\xd3\xbd\xe8\xf9\xfao\xb3\xbc\xb3\xebf\xc2\x82f\xe6\x9b\
+\xbd\x00X\x1d\x85\x0f\x1fR\xb6\xd4`\xee/\x11\x1b\x13\
+*\xcb\x83\xf9gP\x17\x9f5\xb5H\x8d\x04\xa1a\xb3\
+\xf8\xcdo\xde/\xef\x1c?\xceT\xc7\x8d\xe6\xc7\xd7\xec\
+N\x1b\x99]\xfd\xbf\x1e0\xb7\x08\x00\xe3C\xb4\x06\x9e\
+\x10x\xf0\xf56\x8a\xd9\xc0\x22\xbc\xa3\xc1M\xd36P\
+z\xdf\x15?\xa4!mA\x05\x18k\x07\xa2\x85z\x01\
+\xda\x00\x87\xb6\x04e\x89A\xf4\x96,\x87a\xcd\xd1\x81\
+\xa5\xcb\xa9\xe3\x0cY\x18\x0b\x15V\x88\xd2ah\x0b\xe6\
+9\xc0P@I\xaa\xb3\x0c\x91\x16:?\xa1\xe3\xfe\xfb\
+\xd3\xed\x1a\xd8\x86\x92f\x1a\xcc\xd9!\x9f1Tc\x01\
+\xc0XV\xa0 \xccV\x17\x99P\xc2f\xc2F\x1d-\
+:\x9cF\xc85ld\xc8a\x1b\x0a-W\x1f\x001\
+\x19\x81\x8e\x0f\x00DFIG\x82\xc6E\x91\xb7QJ\
+\x01\x0eY\x00!\x0e\xcd\xa4\x81A\xa0\xad\x16z\xf5b\
+l#\xeaB\x8f'\xf4\xe7%\xf3b\xb0\x90d\xd9\xd7\
+\xbcH\xf0\xc1\x9dY\xfe\xcd>\xb6\x19\xc1\x99\x16\xd5\xcb\
+\xb1SW?K\x9c\xd9\xbe6.\x92\xeb\x09\xa33\xb4\
+n#b\xcb\xcc\x01\xf1\xa2\x14\x80:H\x07G\xd5v\
+\xb8hk\xd4`\x8c-l\x1eY\x88\x86m\x10\xc12\
+\xd2\xa4\x96(\x87\x1e\xeb\xa1Ln\x14YmB\xac\x96\
+\x1e\xfa v\xf3z\x1c\xc5j1\x8b^\xe9\xde\xd8\xde\
+e\xd1y\x1f\x94\xf9\x83K\xc3\xbe\xcd`V\xce\xc7C\
+o\x99\xa1e\xecM\x0c\x82d\x83D\x03Bv?\x8c\
+&\x9c\xcc\xb6\x8dd\xc0\xa8\x98\xb7t\x01\x8aC\x84h\
+\xacH\xd1E\xcd\xc0H&-\xe9\xc8\xe2\xda\xa1,.\
+\xb0#\xa9\xe4\x8a\x13\xd2i\xb2\x16\x89.>\x802\x8e\
+\x1aJ\xa2\xe2\x88T[\x00\xcdhhSjra\x1a\
+\xec \x03\x86H\xcc5\x06P\x97$\xae\xd1\x82(\x13\
+(H\xd3\xc8j\xa0\x22\xdd\x0a\xf0j\x98(9_4\
+9\x1cTG\x19f\x1e\x87\xd9\x00%\x8b\xa5\x0d\x12M\
+\x9c\xd3\xa2\x00\xa2d\xee\x080#\xd3Ad\xb3H\x88\
+\x09\xf9zN\xf6\x8e\xf17\xbcq\xfc'\x13\xa8\x0c8\
+\xe5@\xe3\xc9io\xfc\xf0|\xb2\x9f\x9d\xa1\x03\xff4\
+Y\xd3\xd5\x0fz\xd7\x0e8\xef\xb8\x168Y\xd5\xe8;\
+\x95 \xd6.S\x80\xd9\x000[y\x06\xb0\x1e&\x1f\
+D\xa7\x95\xcb\xee*m\xd8\x80t\x5c\xaa1\xbc\xb2:\
+V\xf6\x82u\xc6\x81G\x9d_\xd9\xaa\xc4USch\
+\x8e\xaez\xf9\xc2?\x17S\xb7W6p\xa7\xb2\xfe\x1e\
+\xcazd\x01C\x80\xf0&\x22]\xa9AY\x19\x95\x8d\
+\xf4\x01\xb1RvH\xbb6*[\xea\x80\xd6\x19\xa5\x1d\
+!\x96v%\xee\xde\x95\xe5\x9d\xca\xe6\xfb\x8c\xacr\x01\
+,^\xca\x06t\xd5tPVK\xa1R\x7fD\xea\xff\
+\xd5\xa8W\xa3\xb2^\xca\xd6\x19\xc3\xc8\x8a\x8f#\xab|\
+{e\xafQPw)\xe88S\xf0\xf2\x85\x8f.\x03\
+\x09m\x0c_z\x90\x1d\x05)\x0e\x00;\x98\xd1\xf4R\
+7w\xd05\x072\x1a\x0dV\x1e\xb1f\xa4\xc2\xa5u\
+\xf0\xf2u\xe7\xcd\xb5\xfe\x8f^.\xa4\x82\x1e8\xbe]\
+\x8da\xff\xec\xdd\xeb\xd5\xe3\xd5\xf7\xab\x1eJ\xea\x9b\x92\
+g\xa7\xffZm\xca\x81us\xfc8\xfb1~\xf7\xe3\
+\x06(\xea\xfa\xc7\xa0\x8f\x9f\xbd;?\xdf\xc6\xfe\xd9o\
+\xc4\xd4o\xc8\xd4\xeb\x845\xba;\xa9\xb8\x1d\x12TZ\
+\xa8\xe7\x1dq\xcey\xde\xfb\xad\x83d5#\xbd\xb06\
+\x90\xc3\x8a\x9c\xeb\x17%\xea\x17\xd7\xb3&$\xf3\xf9\x87\
+\xe0\xa7;\xcbP'\xfb=\x16\xea\xf70\xe1\xf9\x05+\
+\xd2\xfb\x9f \xdeG\x05\xff\xfb\x9a\xfeL>g\x9a\xb2\
+\xf7\x08N\x1e+\xbf\xad\xc9\xc5\xcd\xd3\x03\x22\xd7\x07{\
+\x10v\x953\xcd?\xa6\x13\x1f\xc3tb\x8f\x11\xa7\xcb\
+\x13\xc2q>\xf1\xfe\xf3\x87\xad\x99\x07\xa6\xf3\x94\xfd>\
+\xc2\x19\xc0\x95\x92\xcf\xac\xa0}\xebc\xa2\xc4\xc9Q>\
+\xaa\xfa\x7fYU\xe7[\xaa\xca\x93\xaa\x1fU\xf5\xbf\xac\
+j\xc0-U\xd5I\xd5_\xe1<`\x07\x13\x08x\x99\
+\x8ak\xd4\xf7\x07\xf3R|\x9e\x0fYn\xaa\xeb?\x86\
+r\xbcz?\x15\xe4\xc37C/\xfdp\xe2\xf5\xf1\xb3\
+\xd5\xeb\xf5\xb7E\x0f\xea\xd8\xae\xaa\xad\x06\x14\xfa\xa3\x9e\
+\xd5\x1a\x90\x99\xc5\xe1\x10\xf9\xa2\xccd\xbc\xc6\xceS\x1f\
+\xe2pf\x15E\x95\xa6\xaa\xc0\xc3\xe6(&!\xab\x87\
+4\x8b\x98\xc4\xa27\xad\xc0$(\x19\x13\xfeiN\xea\
+\xa4\xf65\x1c\x1b\xc7e\x8eK\xa2if\xda{1}\
+V_\x89\xdc\xa0\x03\xed\xaf_\xf5\xa7\xc7\xb2\xc1^\x1c\
+\xf7\xef8\x9f\x9d\x1d\xff4vmD\xaf/J\x95\xf9\
+\xc1|\xa2\x1d\xde\xa2R7\x8dQ\x02 %\xbd7\x8b\
+\xf8d\xad \x89n\x08X\x183\xa7\x8f\x95f5\xc3\
++\x91R\xb5\x0c\xaa\xd5e\x904U@\x91C\xc3f\
+,H\xd9\xb1C\xa3\xe6!`<4\xcaK\xebY\x1b\
+\xa1&\xfb\xf6Y\xd6\xd84\x1c\xd7r\xbd!\xd5U\xc6\
+k\xa2Vs\x8c\xdd\x0e\x90\xc6\x1d\xb3\x06aX)\xac\
+1\x93\xb0\x0f\x18\x19\x90V\xc2v\x0c\x1fO\xd5\xe4\xa1\
+\x8f\x89\x0dA\xc1\xea\x8d\xd2\x92\x9c\xb4\x0a@\x000\xa1\
+\x8eqS\x0e\xc2\x83\xacFO\xf1\xf5\xa2\x84#\xe9\xd8\
+\xf0h`c\xa3\xcc\xd4m\xad|d\xa3L\xe8=p\
+m\x89\x1c^\x9c\x9a\xb9\x1a\x17\xe4\x9c\xae\x5c\x9d\xa7@\
+\x1aN\x14\xb0Q\xa1\xca?\x92\xccU\xa4\x80\x93K\xd4\
+\xf433Y\xa3.(\x86^YY\x0e\x03\x8a\x8f\xaa\
+\x88\xb8\xd98\x06,\xa3\xa0\xa2v8F\xb2\xeen5\
+\xa4\x0a\xb7I\x98\xe4\xa0\x1f\xf8\xc5E\xedPa\xe0a\
+\xbbs\x8a\x1b\xc2.\x948T\x1ax\x15\x9bT-l\
+\xe4\xbf\xb8\xa8\xb3.h\x92l\xc9\xbenp6\x8bM\
+\x03+\xb8\x8e\x94\x0b\xfaa\x8d\x02:*\xd5\xd0\xa8\x86\
+Q\xc7\xb8\x85G\x10\x96\x5c\x1fz\x84<\x8e~!\x00\
+\x1c\xce\x83Q:b\xd4\xe0\xa7:S\x87\xb0\xa5\x199\
+O\xb6\xbd#>\xb3b^\x80\xe77\xecT\xda[\xe8\
+\xec\x5c\xbb@4\xc8\xf1\xc0Cl\x88\x18H\xf2`\xdf\
+U0b\x83\x07\xef\x1dK\xca\xbb\xe7\x81$\xfe-\x01\
+d\x1e(\xcai\x1c$\xab\xd0\x0dI\x95u\xd5\x8d\x8a\
+\xeaC\x89\x8c\x9c\x96X\x96Qo\xd4\xc2X\xc41u\
+pM\x17\xc5\x0e%\xb9\x12\x0c\xb3S/\x87 \x1c\xa2\
+\x87\xb1\x00\x96\x9d\xa25#\xc8\xa4\x0a \xa2\xe8f\x07\
+\xa8c8\xe7\x8a#\x96 9\xc3\xa4\xec\x88\xc9\xeb\xec\
+\x09\xd5\xc6\x08\xa8\xbe-\xd1\x9aJD\xf8\xf6\xb5\xbd\xb1\
+\x99q\xb7\xcc\xa9\x9b\x95\xd7\xc1\x19ytj\x8f\xe0\xae\
+9\x124\x84\x90(2\x1c\x12p\x80(\x0dd\x98\x09\
+\x0a\x82\xf2p\xb2z@\x14AR\xfdA(4Z\x18\
+T%ZY\xcd\x85\xb4cuq\x8e5ff>\x04\
+*\x07\x8b\x9a\xe7Lh\x11\x9d@`34\xb4\x09\x02\
+H\xccP\xcf\x86\x12\x8e:C%Z(&\xe7\x1c\xad\
+X\xc7\xe84C\xd9[ \x88\xf8\x0cEj\x94\x1c\x94\
+\xa5W\xa1\x8eF\xe5nH\xce\xa6\x07\xa9M\xc1A\xab\
+fj\x00\x11\xaa\xa5\xbf&F\xd5\x9a\x0d\xd3\x82T\xc7\
+\xd9\xb5\xb99Z\x99H\x88\xa7QE\x03\x111+\xfb\
+\x920\xa2\xdcer?\x1f\xf4\x94e\xcd`\x88\x11\x84\
+\x8d$\x08J\xa7\x09\x15j\xa2\x9aZ:Y\xb4\x94\x0c\
+\x90-\xf4\xa8PJ7\xd0-\xd4\xbc\xb9\x93J\x14\x96\
+l\x9c<a3\x9e&t\xe2\xffh\x1b\xb5\x06`A\
+1]\xa70\x09'\xf3\xa9G\x13\xb6\xdd\xfb\x05\x14\xc1\
+\x9a+\xc9\x90p\xa3\x11\x09\x0aN\x96\xdf\xd1\x1d\xac\xec\
+\x0a{\x0ct\xf3\x07\x14\xa1\x7fZ\x9c\xb8\xb2\xd1\xc7\x1c\
+\xba\x96k\x1f\x0b\xbc\xa1\xf6qM\xcd\xb1\xf6\x910/\
+J\x19\x08T\x0a\xa3\xe0\x94\xa8:\xc4\xd4H\x0br]\
+\xf3NZ\xa4{\xac3_%\xa0A\xb0\xd6\xa1\xb1\xe0\
+\x88\x80\xd4\xa8Lg\x1e\x1eY\x98X\x92\x0e\x99\xce\x93\
+P\xf3\x9e2\x9d)\xdec\xa6[.\x9a\x97\x93\x9f\xfc\
+\x91\xfc\xfeH~\xf7\x93\xfcB\x9aY\x8a\xddmF,\
+\xb4\x83\x98\x7f\xe4\xc9\xdfI\x9eT\xc8Y\xac\x9bG\xd4\
+\xd0y|p\x9c2\x89\xe3\xd5L\xe2p5\x93X^\
+\xca$\x0e\x973\x89\xeb\xe5L\xe2z5\x93\xb8^\xcd\
+$.\x973\x89\xcb\x95L\xe2t5\x938N\x99d\
+1\xc6\xf3\xb5\xf5\x81\xfd\xea\xeb\x83\x9b\xf3\xe4\xc3)Q\
+\x226\xb7NX\xb4\x08\x17\xbe\xcdd0\xf6c\xec.\
+W\xee\xe6vL\xd2d\xed-\xe0\x5c\xde\xe2\xcdj\xf1\
+\xcdE\x98\xf3\x02\x80h\xa4\x1a\xd8\x81\xf9);\x5c\x89\
+\x98\xb8\xc0k?\xd5\xba\xb9\xae\xb8\xce\xe4\x1c~\x9d&\
+\xe7D\x97JR\xc468\xbc\xe6f\x19e{q-\
+\xd6\xabd\xb8\xb5\xb8\x16\xd3\xdaZ\xbd\x96\xe1m0\xfc\
+\xe1\xb4\xc26\x9d\xba\xb5\xc2\x96\xdb\x0bl\xc8\x0d\x18\xa9\
+#\x17\xab6\xa8\x0d\xd5\x0a\xbaXM\x9a\xf7oW\xe0\
+t\xd0\x0f\xf1\xa2l\x16B\xcb^t\x8d\x11\xd0G\xe2\
+EW\x9cf\xc9\xad&/\x1a\x01\x8f\xe6\x8a\x19\xba\x01\
+$+C\xa1\xd1\x15O\xdc\xc5<\x13,\xb1\xb6\xf7\x02\
+\xf6\xee@\xe5\xf2\xe0\x12k\x0dP\x9c\xd5o\xc9\xde\xb2\
+kp\xda\xdc5\xc6e\xc3N\x04\xd3\xb0\xca9_\x9d\
+\x85\xf1Yb}\xc0v\x12\x94\xf2\xde\xa6\x09-\x093\
+E\x0f\x1ffc\xb0\xb1\x0al\x99$d\xf9`\xb96\
+\x88\xd4\x0f\xec\xf7=\x8d\x9d\xfd\x02c\xf7\xe1D\xef\x9f\
+M=>\xe68\xa0\x8c\xa5\xa0qKd\xe3Z\x07g\
+lL\xaa4L\xe6\xdcI\x9c\x0e8\x9aJ\xa8\xc7\x10\
+\xc29\x01\xe4@\xac\x81&\x9a_`\x5c\xf546u\
+\x0a\xc2-\xd4\xb5%\x03a\xd6u\xc80C\xc7\xceh\
+\xb6\x84p\xe2\xdbE\x98H\xdf\x7f<\x02>\xda\xf1\x08\
+k\x8e\x95\xf3\x08\x1a{\x10nc\x12-D|\xf6>\
+\xcd\xc6\x86\x149\x96\xe2\x04L0\xce\xcb-\xc0\xa3\x06\
+\x03\x0c\xd5\x86\x94i\xa0\xc8| \xda\xdc(\xd1'\xac\
+\x06=\x9a1\x0a\xdb\x16\xca\x95\x03\xd3u\xc00\xc8{\
+'I\x1bE\xb8\xd6Hr*\xa0\xfc\x06\xec(\x11n\
+aG\xf4+\xf3k\x8a\x86D\x198\xd4O\xe6F^\
+c\x14\xe2$Y\x98eP\xd8\x01k3u-$\x89\
+\xc0\xa2\xc6\x92\x1aZ\x8ax\xa1\x0eb\xce%\xcf\x15(\
+\x0bbM\xe22\x0e\x19B\xe9p]!\x91X\xcfp\
+=\x0a\xa52\xc7t\xd0\x19*\xd9\x1c\x125g\xa8A\
+\x03%\x8f\xa1\x80\xa4&\x0cY\x86\x88\x8d\x9cI\xf1\xc0\
+\xbd\x05a \x95\xb93\xa1\x99\x0c\x13HL\x1c\xf5F\
+V\x87\xf5W4P!M\xea\x9d\x14\x0a)\x07\xc1\x0d\
+B\x98b\x90\x08FA{[R-j\xec&\xcb\x9b\
+\x00)p\x99.\x90\xa8\xea\x01gC'\xd5,\x0c\x01\
+\x1d\xfd@\xa0\xa5:\x04\x0f\x98\x92\xda\xe06\xd9\xea(\
+E\xa1\x90\x0eI\xa3DT\xb2\x01cp\xd3\xabW\xde\
+m\xd9y\xf1]\x88\xfaW7\x80\xf9\xfc\xff\x01\x9e\xd5\
+\xc9%:P\x01\x00\
+\x00\x00\x18\xd8\
+\x1f\
+\x8b\x08\x00\x00\x00\x00\x00\x00\x00\xed}ms\xdbH\x92\
+\xe6\xf7\xf9\x15:\xcf\x97q\x1c\x99\xca\xf7\xccrw\xcf\
+F\xdcL\xec\xc6F\xf4\xc6]\xec\xce\xc4~\xdc\x90%\
+\xda\xd6\x8e,)$\xb9m\xf7\xaf\xbf*\x90\x12X\x14\
+-\xd0l\xda\xdd;c\xb8\xdd&\x1eTUV=\xf9\
+\x06\x14\x80\xc2\xf7\xff\xf4\xe1\xed\xc5\xd1O\x8b\x9b\xdb\xf3\
+\xab\xcb\x1f\x9e\x11\xe0\xb3\xa3\xc5\xe5\xe9\xd5\xd9\xf9\xe5\xeb\
+\x1f\x9e\xfd\xf5/\xff<\xcfgG\xb7w'\x97g'\
+\x17W\x97\x8b\x1f\x9e]^=\xfb\xa7?\xfe\xee\xfb\xff\
+5\x9f\x1f\xfd\xe9fqr\xb78;z\x7f~\xf7\xe6\
+\xe8_/\xffv{zr\xbd8\xfa\xc3\x9b\xbb\xbb\xeb\
+\x17\xc7\xc7\xef\xdf\xbf\x87\xf3\x15\x08W7\xaf\x8f\x9f\x1f\
+\xcd\xe7\xb5\xe6\xedO\xaf\x7fwttT\xc5^\xde\xbe\
+8;\xfd\xe1\xd9\xaa\xfc\xf5\xbb\x9b\x8b\xa1\xdc\xd9\xe9\xf1\
+\xe2b\xf1vqyw{L@\xc7\xcf\xc6\xe2\xa7c\
+\xf1\xd3&\xfc\xfc\xa7\xc5\xe9\xd5\xdb\xb7W\x97\xb7C\xcd\
+\xcb\xdb\xdf\xaf\x15\xbe9{UK\x8f\x9dy/C!\
+*\xa5\x1c#\x1f3\xcfk\x89\xf9\xed\xc7\xcb\xbb\x93\x0f\
+\xf3\xbej\xed\xe3\xb6\xaa\x8c\x88\xc7\xf5\xd8Xr\xb7R\
+/>\x5cT&>\xd9\x99\xe1\xe8\xba\xf4\xca\xfeu\xfd\
+\xfbP\xe1\x1e\x80\xdb\xabw7\xa7\x8bW\xb5\xe6\x02.\
+\x17w\xc7\x7f\xfe\xcb\x9f\x1f\x0e\xce\x11\xce\xee\xce\xd6\x9a\
+\xb9'\xbf\x93\xdbi\xe4\xf2\xe4\xed\xe2\xf6\xfa\xe4tq\
+{|\x8f\xb7\xfa\x9d94\xa0j\xf8\xec\xeeM\xdd\xe5\
+\x1cv\xdf,\xce_\xbf\xb9\x1b\xf7\xcf\xcf~xV\x07\
+\xcc\x8e>\xec\xdfw\xe9\xc5CC\x08\xc2\xc3\xa1{9\
+\xeb\x87t\xa3\xd6\xd9\xd5i\xedY\xed\xf7\xeb\xab\xf9\xe5\
+\xe2\xc3\xdd\xfc\xa7\xf3\xc5{\xa8\x02~\xee\x9b\xb8zw\
+w\xfd\xee\xee\xbfj\x89\xc5\xe5\xb2\xad:\xa8q\x84\xcb\
+\xc3C\xbd\x07\xb0o`\xf1\xe1\xfa\xea\xe6n\xfe\xea\xfc\
+b\xb1\x14x\xfc\xe6\xea\xed\xe2\xf8\xfa\xfc\xb2\x8e\xf0\xe6\
+\xaa\xfe8\xbd=\xbe\xfa\xf0\xf1\xf5\xe2\xf2\xb8\xd6\xb88\
+yy\xb18>9\xbd\xab\xb2n\x8fk\xe7.Nn\
+W\x9d\xbb\xbe|\xbd\xb5\xe9\x0fg\xd7U\x8d\x1e`[\
+\x0f\x7f\x1c\x0f\xff\xb1\x1e\xff\xfe\xed\xe2\xee\xe4\xec\xe4\xee\
+\xa4\xfe^\xd1z\x8fh\x0c%j\x99j\xb4/\xfe\xfd\
+\xcf\xff\xdc\xf6\x86\xfd\xd3\xd3\x17\xffyu\xf3\xb7\xba\xbb\
+\xdaZ\x81\x93\x97W\xef\xaa\x82\x86:\xabrg\xa7/\
+\xaa\xe1\xbc=\xb9\xfb\xe3\xf9\xdb\x93\xd7\x8bf\xa1\xff\xbb\
+\x1a\xca\xf7\xc7\xe3\x81\xae\xf0\xdd\xc7\xebE\x05\xbafo\
+\x16K\x0b\xdc\xe6\xb4\xf5\xbf\xb7\xe7\xad\xd2\xf1\x7f\xdc\x9d\
+_\x5c\xfck\x13\xf2\xec\xe8\xf8\xa1\x9f\xc7\xab\x8e6\xa0\
+\xed\x8e\xe3\xa8;\xf7\xc3\x1c\xf6\x1e\x0c\xa1)\xe5\xac\xf1\
+[\xd1u\xf6\xde\x9f_\x9e]\xbd\x9f\xdf\x9b\xa1G>\
+\xdb^\xe2\xden)c\xb3\xc4u\xed\xde\xed\x9b\x93Z\
+\xea\x87g\xbc\xed\xe0Uu\x8c:\x9ef\xa0\xb8:\xfe\
+\xfa\xdd\xf9\xd9\xe2\xee\xeabqsr\xd9H\xa0\xb5C\
+7U\xd4\xd6#W/\xff{qz\xb7\xfd\xd8\xcb\xab\
+\x9b\xb3\xc5\xcd\x83$\xda8pzuqu\xf3\xc3\xb3\
+\xdf\xfb\xb0\xad\x0e\xb5\xbe\xdd\x1fx5l\xcfFsy\
+yr\xbbX\xed\xde\xbe\xb9z_{U\xc1\xbb\x9bw\
+\x8b\xcd\x11\xfe|u\xf5\xb6\x0d\xcd%\xd1D\x1e\x11x\
+\xfa\xe1\x87gs\x12\x04\x16\x11\x7ft\xb4u\x96\x02\x9c\
+)?\xc5}m\x00?q\xa8\xd6V{\xdc\xe8\xbb\x9b\
+\x9b\x1a\xf4\xabW}\x5c\xdc\x8c\x01e\x1c\xcd@\xff\xed\
+\xf6\xf1\x0c\xc7\xe6/_^}X\x1d_\x99\xd9X\xa0\
+Rqo\xce\xd5J\xab\x1e\xaa_W\xac5\xf4@_\
+\x03X\x99\x1f\xc0\x9f\xceo\xcf\xab\xd3\x8f2\x87\xadF\
+\x8b\x0a\x9em\xa0-\x92\xd6\xacY;\xc0\xe0u\xbb\xfe\
+\xb0y\xe8\xe3\x96C\x8b\xb7\xd7\xab\xa3\xf5`\xf3\x97\xc1\
+\x1f\x1e\xbb\xc0\x80\x9f-^\xdd\x8e\xcan{\xec\x98\x8f\
+\xc6z]c\xeb\xf5\xe2\xb4\xa5\xc7\x95\x981\xb8.\xc7\
+\xde\x17\x95\x91\x851>_\xffW\xd3\xe0\xd1\x8b#\xd7\
+\xfa?\xdaZ\xe2\xe3\xb2\x04!\xb6\x7fpk\x99\x9f\x87\
+<\xb1\xad\x9d\xbe\x0b\xf3\xab\x9b\xf3\xd7\xe75\x88\x0f\xe5\
+tI\x94G\xab\xd3\xe9hmpZ\x1e\x02\xcc\xf75\
+\x87.Nn\xfe\xe5\xe6\xe4\xec\xbc\x1a\xd1z\x85\xfe\x88\
+h\xad\xf5\x10\x94n\xef\xae\xae\xdb\xef\xb1tCDm\
+\x94Y\xb9\xbb\xfbx\xb1X\x1e\x99\x0f\x9e\xf7\xe2\xf78\
+l\xdf\x0d\xd0\xca}_\xacW\xb9z\xf5\xeavQC\
+\x13\xd6\x0eN\x0b\x93\xcf\x17\x86[\x84\xd1\xc8\xc6q?\
+\xe8\xcf\xe5\x88\x8bMr\xc4%\x9e\xee\xb6\xcb\xa2\xbcz\
+u\x18\x8e\xb8\x94'\x85\xad\x22\xe1W\xe5\xc8q\x9a#\
+\xe7\xe9n\x1f\x8c#\xd7\xdf\x1cG\xc8\xd3\x1c\xe1D\xb7\
+O\xcf\x16\x8b\x83q\x84\xfe\xb4\xb03^\xe4\xe2\xec\x00\
+\x1c\xd5\x93\xca\xbb\xc5\xcd}\xc5\x96M\x11\x08\x19\x03\xc9\
+V\xed\x8d\xe7\xd5\xc0\xa8\xa48\xc6\x81\x8f\xcb\xf2\xac\xc5\
+\x1c\xe9\x1e\x1eO\xbc\x81\xb5\x14\xe2.0.%\x8ad\
+\x8c\x9c\xbfZ\xfc\xcb\xc9\xbb\xdb\xdb\xf3\x93\xcb\xffs\xf1\
+\xae\xf5f3\xf8\xd6a_,Nk\x93'\x17\xefO\
+>\xdev\xe4\x9c\xfdy\xf1\xd3\xf9\xc9\xdd\xfdY\xba\xba\
+\x99\x17z\xd6\xd3\xdaK\x10\xc9\xb2\xc6\xcc\xb2K\x9fi\
+5\xae\xa8SV\xe3:\xa5HLZ\xc8\xe2 V\xe3\
+\x8a\xf9\xb40\xc98[\xe8\x84\xb0Cz\x96\xd7m\x92\
+#\xf7\x89n\x97W\xa7\xf4\x8a\x0f\xe3Y\x94\x13Y\xcc\
+2\x16/m\x07a`;\xa8\xc4\x03'\xf4\x7f\xca'\
+\x96\xdb\xc4\xed\xef\xc8\xbd\xa3\xd1\x17r4scK\xce\
+\xa7\x1d\x8d>\xd7\xd1>\xd0\x0f\xcfB \x94]r\x0c\
+4\x15%\x03\x117\xd5\x07\xf4\x03\xd7\xb2\x09E\x8aH\
+\x19\xcbV\x94\x1c\xc20lD\x1f\x1b\xa7\xa9\x8c\xca\x19\
+&7^\xbc\xb9Y\xbc\xaa\xd7+\x8fR\xe8X\xee\xf5\
+\x0a\xfc\xeb\xe5\xf9]=\xc7\x7fw\xbb\xb8\xf9\x8f65\
+\xf1\x7f/\xffz\xbb~\x8e]\xa7}\xce\xfemq\xf7\
+\xe6\xaa\x8a\xad\x8d6N+\x0f\xd3#W\x08\xebG\xad\
+\xc0a\x99\x07\x1c5\xef6\xeab_k\xd4\x14\x05(\
+\x98\xb5\x1f9\x0b\x84\xa5\xf7#\xa7\x10(\x89A\xdd\xc8\
+\xdd@5\x89\xf8\xe9\x91\xfb\xc1G\xde\x97\xfaK\xbdv\
+\xbem\xb3\x14\xf5\x9a\xab\xfd\xbc\xa8\x13\x8f\x7f(1\xc3\
+\xe7;\xd1P\x1c\xd4S\xbd\xa3\xc1\x05H\xd2\xc8:\x1a\
+\x8c\x81)\xd5\xb3\xa7\xc1\x81Y\xd5\xe2\x09\x1a\xb8\x98\x1c\
+\xca\xec\xa7i\xa8\xf357\xe7\x1f\xfe@\x80\xec\xc5\xd1\
+gX\xff\x8c{sF`\xd4\x882\x9b\x13\xa4\x98\x87\
+\xf9Nde,\xb9\xeac\xc4\x1cA\x9c4\xd9\xbc\xb7\
+\x1ar\x08J\x8d\xde_\xd0A9\xcc\x9f\xa6\xcb\xff\x0e\
+\xe8\xd2\x80\xe2\xa9\xc1\xbd\x8b\xf9\xd2\xb6\xa2#K\x0b0\
+\xf5e+\x9a6pe\xe5i\xb2\xcand!\x7f\x15\
+\xb2\x0c\xeb&\x8d,/\xa2,\xb4FVk\xf0\xe4b\
+\x93\xac6\xa3DkCosH1Z\xc0Mu\xbd\
+q\xefU+\x9c\x09\x99\x142\xa2\xb5\x0a1\x81*\x93\
+tl\xf5\x22\xb9\xf8N\x9e\xe8u;\x18[\xf3Gt\
+\xa1\x9b\xfb\x8c\x99A\xc2)*Y\x0c\xea^<w\x22\
+kN\x0e\xee\xa9\x99\x1dg\xe9\x90M\x8e\xadS\xe78\
+\x92\xd4U\x1d\xe1\xae\xea\x93\xdc\xe5N\x96\xa6Z\x0e\xc7\
+\x1d\x02[\xbac%\x09\xc1\x98XC\xdb\xcf\xe4PQ\
+\x9d!(\xa1#\xd1\x8c\xd4\x81\xa5\xa8\xcd\x0c\x01\xcd\xac\
+\xd8\x8e9@\xc1\x1e\xa5\xc1\x82\x9e\xd2\x074T`\xeb\
+\x1c\xd4\x80\x9a\x98\xe4'\x1d4h'\x93S\xd4\x03\x9a\
+\xdc`j\x08\xe9\x84\xea2cJ\x88\xe6\x94*N\xe9\
+1\xc9\xcc\x10\xac\xa3\x0f\xe9s\x16\xef\x89B\x1d\x0b\xd0\
+X`:C\xe3l\x9eM]\x9c\xa2\xcfw\x1b\xf6\x13\
+\x1c\x0b\xeeh\x9a\xc8U\x17\x13'\xe0\x13\x1e\xd8\x9cJ\
+\x0c\x948K\xe9\x9c(\x1c\x0a\x9a\x17\xe9=\xae\x00\xc9\
+\x90A:gu}\xd6\xb94\xe3n\xc4\xcd\x89\x0b4\
+)\xd5\x0b\x18,\x8d\x8b\xe8b\xee\xfb\x90\xd8\x8fOp\
+\xb7L\xc2\xa5\xe0g\x90xzq~\xfd\xffN\xee\xde\
+\xac\x0b\xbe\xc7\x04mt\xa8{pk\xbf\xc7\xeb\xa9\xeb\
+\xb1\xad\xf1\xfa\xee\xe1:\xee\xbbz\xbds\xf1\xa2\xde{\
+\xfa\xc3\xef\x1f\x8f\xee\xf9pt>\x16\xbe\xbd\xbb\xb9\xfa\
+\xdb\xe2\xc5e\xbd\x7f\xbd\xfa\xbd\xbc1\xf4\x02\x81$\xb4\
+\xd4\xed\x1eo\x1c\xd4\x81\xbe\xb8\xb9zwy\xb6\x0e\xfe\
+\xf7\xd5\xf9e\x8f\xd6\x9b]\x8b\x9b\x8b\xf3\xfa\xcf\x0b\xbd\
+\xc7\xceN\xea-\xa5\x9b\x9b\x93\x8f\x9d\xb0\x86\x0e\x97\x9b\
+\xb5$\x90\xad\xe0\xadW\xa5\x95\xb5\x7f;\x9a\x13\x13p\
+b\x99q\x013t\xe5\xa3?5\x94\xc1\xdc\x9cg\x9c\
+\xc0n\x85\x1b\xa6\xe0\xe2\x16\x1dfP\x98x\xbd\xfa\x8f\
+\x15\xd6{\x8b2\x06O\xe7\xb2\x84\x83\x81\x0a\xdb\xa6\xb0\
+\x10\xd0\x08\xa1\xf5\x86\xc3\xc04\xd9;\xcc!\x85Kn\
+\x08K\x01C\x97\x9980K\xf1e\xa3\xa9\x10\x85\x90\
+g\x12`\x98\x9e\xd2\x81\x05,\x8asY\xab\xaf\x08\xe9\
+*64\xea\x08\x12\xe86s\xed\x84d\x00\x89\xb9\xf5\
+22A\x19%h\x1d,\x08ZH(\xd6\xaa\x17\x82\
+\x08G\xc9\xd6\xe88\xa0\x92\xa0Z\xc4\xb25;\x8e\xbd\
+\x14\x08Q\xcc5\x8eFl\xa4s\xac\xde\x93\x1f\x06B\
+\x12\xd8\xe0QU\x9d\xb0Q\xabc\xc3\xa3\xf6Gl\xb4\
+\x93^\x16)8\x86\xe58\xb0\xa1Q\x12\x10\xe6\xf4\x91\
+\x83updkl`$\xb65+\x01\xa1\xc2R\xd9\
+\xef\xc4\x8c:\xea\xa4\x8c\xda\x1c\xc1Q\xef\xeb\xf5G\x1b\
+\xf9q\xab\xed\xff\xbc1\x13r\xbd\x8c)\xb16\xffq\
+\x1fU~\xcb!\x9d\x99@E\xedK\x84t\xf9\xea!\
+]\xcb\xde!}S\x91\x8f\x03 \x93\x80[Z\x1f\x93\
+\x98\x14\x8a\x0a\xaf\x07@\xa6\x00$\xa5\xe8\xb0\x04.\x99\
+\x1b\x01\x90u\xc5\x7f\x1f\x00\xd9\x15,\x90\xca\x8607\
+H\xd3B\xeb\x0d{@aK\xef\xb0\x02Lh\xb9!\
+,\x0c2\xa5\xf4\x01\x90#\x80<}=\x00\x8e\xe0\xe8\
+2c\xfd.\x00\xb21\x84&\xb7\x00\xd8\x09\xb9\xf7\xd3\
+^\xc6\xe8\xd2#8:\xffX\xbd\x0b\x80\xe3\x80\xba\x98\
+4\x8e}\x8c?#G#6\xd29V\xef\xc8\xef\x02\
+\xe0\xa8\xaaN\xd8\xa8\xd5\xb1\xe1Q\xfb#6\xdaI/\
+\x0b\x1dJ\x1am\x04@F\x83@\xd4.\x00\x8e\xe0\xc8\
+\xd6\xd8@\x17\x00Y\x10\x881[\x00\xec\xc4\x8c:\xea\
+\xa4\x8c\xda\x1c\xc1Q\xefc\xfd.\x00\x8ec\xea\x02\xe0\
+~\xa7B\xf2?\xeeT\xe8\xd3!\xbd\x0fe_\xfa\xea\
+c\xff\x0b\xa3'\xaf_8\xfe\xf1\xae_\x0a\x01R!\
+\xe4C$:\xca]\x13\xdd\xdf\x0f\x81\x99\x87$\xd0\xff\
+\xfe\x09\xecO\xb7\x0eL \x96\x7f<\x0b,zH\x02\
+\xe3\x1f\xd0\x02\xed\x90\x04\xd2\xae\x04~K\x22['\xb8\
+\xcbA\x93\xc8?\xe4%'\x97\xc2\xbb\x92\xf8m*\xf6\
+\x93$\xe6\x97\xb7\xc4\x03\xdeo\x9f+B\x98&\xcb&\
+\x13_KS\xdbI\xd4\xafi\x89*\xa0\xa1\xe1\xdc\x8d\
+\x8f\xd0\x80C\x95\xbb\xf1\xb9C&%Z7\xbc$\x10\
+\xb6\x08\xebF\xe9:\xf1\xb8\xc7\xf4\xed \x84 \x16\xcd\
+v\xe3\x8c\xd8\xd1\x92f\x08$j\xc1Z\x7f\x85\xa3:\
+\xc5,@\xdd\x8c\xd1f\x8c@\xa1$\x87\x08\xabR\xbe\
+\xe9\xe1\xf3\xf40'D0Q\xcdC*B\x88\xf2k\
+\xcf\x06\x12\xe9An\xf0\xf4s\x19\xfd\x98v\x9f\xcb \
+\x07-\x86\x88\xa9\xbf\xad\xfb:^\x00\x8b\x84\xcd\xd8\xc0\
+\xa5\xe0r\xa61\x08XRK\x19\xe0\x82%\xb3\x82\x02\
+\x1c\xe9<c\x07\x0b7\xb1\x8a)D\x12\xd7\xea\x09\x84\
+\x11e\xa8^\x08\x02\xadH\xce\xd4\x00\x855l\x04\xcb\
+,\x18B\xcd\xb1\xac\xd5\xcf\x02\x1e\xc8\xc5[}\x0bH\
+s\x0b\x9e\x11:X\xd1R\x8b\x0a\x02\xa9\xa8\x94\x07\xb0\
+\xf6\x89\x04\x982\xfa\xfa\xb2\x14\xf4 'W\x08?t\
+\xc7\xc7\x9a}\xcf\x9b\x9e\xd4\xc9u\xc6\x0a\x99\x051\xda\
+\xf4\x17\xb89\x935\xb0\x109g\x05\x15\xeek?\xd0\
+\xc69\x90\x996\x13\x02qW\xf3\x01$d%\x9d\xc9\
+\x927\x8c\x87\xdaR \x8a\xf1J\xb6\x02;\x91\xe8\xcc\
+\x10\x14\x0b\xb9\x8d \xcd<*\x88\xe4\xa3\xecH@R\
+\xd4\x8131\xd0\xac\xdbpm\xe9\x1c\x85\xablc0\
+\x22\x92{\x8c*\xe6\x02\xc4\x1b\x95C $3\xca\x83\
+\x94\x18\xc1|\xe8O\xdc\xd7\xee;\xee\x08^\xc8\x0a\xcd\
+$@\xd9\xa3P\x15]\xa08\x15\xe3\x99\x08D)\x82\
+\xad6\x01\xe7P\x1f\x9b\xba\xee\xeb38\xa2\xb5\x98\x13\
+`J\x1c^\xc1fx\x85$\x9a\x09\x9a!\x9bn\xb5\
+\xd6\x9f\x8f\xaa\x15\xab\x0e\xbdb\x07\xb1{Uj\x02\x9b\
+&G\x83[\xafHj\xaf\x08\xcc(\xb2\x82\x05\x92I\
+\xdd\x96`\xebe1\xb26U\xbeB\xdc\xee\x91?-\
+\x11I6\x9ey\x82\x9b\x0bQ\x05\x11\x88B\x93fA\
+@I\xa1Z\xe5\xc6\xca\xa4\x83\xc1\xf4\x9e#5P5\
+\xe4Y\x08\x14\xcc\x94\xac\x18\x83\x99\x96|\x00[m\x5c\
+\x9ae_[\x022\xd9(\x1e\x04q\x05\x1d\xd4\x9b\xf8\
+\xf5.\x89w=\xffq\x85\x88\x8cc\x19*\x86ZJ\
+3\xd3\xc2\xaa\x1c\x83\x08N\x16j\xc4\x94\xb0T\xabX\
+B\xac|d}(\x08dF\x85\x9b\xba\x884Vc\
+\xc1\x92\x88>(\x81\x15\x8d\x1e\xabe\xfbm3\x22\xdf\
+2\xc7\xfa\x0f\x91lK\x80\xbb'\x1d8\xd9\xf2WO\
+\xb6\x98\x07\xba\xf5F\x84\x8fr\x14\xb9\x03\xb3c\xf6I\
+\x8a<A\x02\x93\xd7\x93\x14\x05\x82\x12\x16[\xcfR\x14\
+\x04\x85\xc4r#\xd8gBJ\xb2\x8fij\x1d\x1c\xd3\
+\xd4\xd8@\x9f\xa7\xc8\x14JfYOS\xc4\x01\x1c\x16\
+\xde\xa5)B\x10#\xdc\xa8_\x04\x94]J\x8c\xa9\xea\
+\x1eT\x8c1[\x8d\x0dl\x8e\x80\x04,R\xcbz\xba\
+\x22J\x88L\xd3\xf5tE\xcc\x80\x18\x22}\xc6\x22\xb6\
+F-\xd1\x98\xb1V\xa0ht\x19kl\xa0OZD\
+ E\xd4\xc7\xa4\xb5\x0e\x8eIkl\xa0O=$\x0c\
+\xce\xc4\xb4\x9e\xb7H\x0b\xb8\x8a\xfaz\xe2\x22G`{\
+\xdc@ d\x10\x8f\xa9k\xc4\xc6\xcc5V\xdf\x1c\x80\
+\x05$K\xe11y-A,\xca\xb9\x9e\xbc\xc8\x12\x94\
+\xc4\xb2\xcf^d\x05B8c={\x913\xa4\x90\xfa\
+\x98\xbd:;\xee\xd3\x17)-\xbb\xd6g0R\x03\xc9\
+ [\xcf`\xa4\x09\x9eJ4f\xb0\x15(,\xd2\xa5\
+\xb1\x11\xees\xd9\x00\x07Z\x978H\x03XK\x9f\xcb\
+Hui\xf7}:\x22e\xb0\x08\xb1\xf5lFR\xc0\
+3\x84\xeeAmX\xac\xcc\xb6\xaf/\xd6\x14\x98\xba\x9e\
+\xceH\xa4\xb9\x0bF\xd7+\x91G#\xf8q\x0d\xee\x13\
+\xdb\x00\xa3\x17ZOl$\x0aJ\xea:f\xb6e\x0f\
+r\xe5O]\xc7\x028\xc3\xba\xd4\xd6\x06\xc6\x82\x22c\
+j\xebT\xd6e\xb7\xdd\xaf\x1e\xf8\x7f\xde\xd5\xc3\x964\
+\xfd\xed\x9ax\xffIs<d\x9a\x9ez5\x8c\x93&\
+\x9b\x99xwl\xfb[_\x9c\xf2\xc4\x9bc\xc2\x19\x9c\
+\x91\xad\xc8\x84\xf4]\xdf\x1d\x1b\xef\x863\x01\x0aq\xff\
+\xb6\x8c\x10H\xb8G\xf6o\xd6 \xb8k\x1aww\xc7\
+\x0bCVPt\xc7g\x87\x81E\x98\xb9\xcc\x08\x0a\x19\
+\x16\xa5\x03<B\xccl\xbb=\xa6\xad\xbe\x8b!L\xb2\
+\xa6\x0e\xcc\x99V:\xda\xacT\xd4\x12\xa3\xa3- K\
+\x08St\xb4\x8dew\xa5\xcdY\xbc\xd2v\x10\xba\xb0\
+|U\xba\x88\x02\x84\xc5s\x07\xba\x18\x81S2h\x82\
+\xae}\x07\xefn\xb1\xd3\xe0\x9dv\x0c\x1a\xfb?\xc8B\
+\xe0FN\x5c\xaaN\x7f\xbbt\xd9\xc1\xe8\x9a\x8e\xb1\xae\
+\x96_$\xc6\xba:>\x11c9U\x952\xf6\x8a\xb1\
+\xdb\xf3\xf7\xf4{XQ\x8c\x90\x96o{\x8c{\xf3\x02\
+\x16\xe2\xe4^\x7f\x0a\x14\x09cy>\x91\xaawU\xf3\
+xR\xc1`V4\xbd?\xa9\xa0\x04R\x93\xec_\xd9\
+b@\xc50\x19O\x1f\xb6\x96=\xed\xca~:o\x17\
+7\xda\xc5\xa6\x8a\xab\xfcb\x17l\xe2\x1f7;\xf5\x96\
+}q\xb5mo\xb1O-\xc5\xb1e\xfd\x98\xefj\x0f\
+\xa7\xa5\xc5\x16i\xf4\xb44\xe7m\xd2\x0e\xb5\x16\x01[\
+\xe4\x14Kl\x89\x9f\xcfR\xc4~,\xb1%\x7f>K\
+\xa7\xf4%YrU\x9f^\xd5Bs\x1f[j\xdb\xe7\
+\xb3\xe4j8\xc1\xd2\x8e\xd2\xf0\x10,\xed\x9e\x1c&i\
+t\xe2}h|\xf9*q\x0f\x1a\x9dt\x82\xc6Ii\
+\x87\xa7Qh\x9a%\x91=\x5c\x12%p/\x96\xc4>\
+\x9f\xa5\x97\xafl\x81_\x90\xa5\xe9\xf0\xee{\x86w7\
+\xb7}X\xd2\xd8\xc7\x96\xce\xaa5})\x96\xd8\x1c{\
+\x96&:c\xe5\xb1\x81LP8\x06n\xe7\x8e\xa5)\
+iy\x86\xb8\x934\xda.M\x0fgK\xa1\xd3\xb6\x14\
+\xfe\xf9\xb6\xf4r\xcfS\x05\xf7\xc8}l\x89\xea\xf6\xa5\
+lIXbzA0)\xfbx\xdc\xe2e\xbe\xfc|\
+\x96\x84\x95~kI\x90K\xc1)\x96j\x99=r\xdc\
+\xe9\xb0\xed\xc8R/m\x9f\x1cG\xed\xcf\x97:\xa1\x12\
+d\x9a\xb4%\xe4=r\x1c\xe2.\x1e\xf7\xd9M \x90\
+\xb9\xb8\xb8~\xf7\x89e\xaf\xb6\x0e\xa0\xec`\xc4(\xb4\
+\xb5Igv\xa5\xca\xd3\x1e\x9d\xf5\x0c\x0e\x0e\xd9\xc5\x89\
+\x90'\x92\xfb\xeeb\xc5\x87?1e'\xdbW;\xdd\
+\xbeN(\x9b\xca\xde\xeb\x8eN\xafg:\xbd.\xea\xf4\
+\xea\xaa;\xae\xd3:q\x0d;\xb5\xdc\xd8\xf4$\xde\x13\
+Q\xc9\x83>o\xf6\xa0\x9f`\xea\x97\x13\x12\x82 \x0e\
+\xf5\xa9\x99\xab\x8a\x96\x04\xf2\xa0\xb0\x9d\xd6\xac!W\xb3\
+\x90\x875k\x8a\x13\xc5L\x1c0\x8bV|N\x90a\
+%\xb0<\xffU\xd9\xcc8\xe4ZX)\xa0\xc4e\x86\
+\xe0\x8ai61\x0d\xd4\xa9\x88\x09\xb0h)\xda\xa9\xa8\
+\xa2\x84T\xd1NCd@YH\xa4\xd3\x10\x07 F\
+\x9an>\xb8yPF\xeb\xf6\xd4\x03\xa8^\xf0\x90\x0b\
+\xd2\xb8y\x88\xd8\xca\x8e\x98\x95\x0b\xc6\x8c$\xc1]\xb2\
+\xcc\xe6\xa2@\x89Q>E\xf5\xf5\xc9Y7\xe1\xa5\x0c\
+\x81(\xc6\xfd\xe4\x18\x0aXb\xf7`\xf4XtD\xbb\
+\xa2[f\xe7\xbe\xac-\xa3>i\xcb\xc5\xf7\x88\x0cR\
+ \x94\xca\xe6;\x95$`\x9a\xd2\xdd0\xd9V\xb6\xa2\
+s\x83\xc2b\x9c\xb1\x9b\x93\xa8\x83\xcf\x18\xc8\x16s\xfe\
+\xa2\xce/\x5c\xec)\xc2\x02e\x0f\xc2\xe6\x8eP\xd4\xd8\
+\xa8g\x0c\x0dR\xd0zG\x9d[\x01%c\xb5\x8e2\
+5p!\xc6\xf85c_\xa0\xed8\xfc\xfdoy}\
+\xa0\xc9\xdbl\x13\xa9\xa7\xbb}\xf7\xeb\xd2u\xa8T1\
+}c\xb5\xd1Fd\xf0\xf8\xa6n\x10`j\xc1\x9c\xb8\
+\x01\xdc\xdd+\xfeUY+\x87eM\x89\x0b\x0a\xb7\x14\
+KEL\xf3\x8b\xe5X\x81\xe2\x84\xdaS\x9a\xc0bE\
+\x7f]\xc7%\xfa\xc6\xe9\xc19\x95\xdf\x10\xa7V\x00\xc9\
+\xbd\xc4\xe6\x0a\xc1\x8e\x8aB\x1d\xa7.\xa0\xa6\x86}R\
+&\x02\xb1B\xfa\x84\xf3O\x9d}m.k7\xcf\xec\
+\x16\xb5\xdb\x89\xae\x03\x9c\xd8\xa8\xb0N\xac\x048.\x19\
+8\xb5\xb6`C\x8bB\xa3e\xcf\xfb\xc0[\x17I\xdc\
+\xb6F\xe2\x9c\x0c\xac_#\xf1\x80\xa4\x89jy\xe2<\
+\x5c\x85ib\xd5\xc9~\x89\xca~\x1d\xcb\x89\xb5.O\
+\xbb\xaaO\x98\xd8\x84\xbdO\xfa\x11WB\x0fI\x1a;\
+>ii\x94\x13+Nw\xabSO\xac\xdf\xdd\xad\xf5\
+=ep\xd3~X\x12I\xb1\xac\xfc\x10A\x0b\x06\x1d\
+\x8a\x9c\xe9+;\x15\xd2\xce\xa2\x8aA\xa0\x11\xd2\xc6z\
+\xb1\x08\x22\xa4\xfa\x895eO\xb7\xd6<\xedj\xfe\x92\
+\xa7t\x18\x0fl1\xc8O[\x0cn.#\xecAI\
+}\xde\xcb\x84\x10\xd1\xf2hubS\xce\xde`\xd2\xc1\
+4(\x7f\x19\x09\x04\xc9\xee\x143\x01\xd1t\xe1\xfc\x9a\
+^\xb4q\xfa\xee\x04\x94\x8e\xee='\x0clE\xc9\xbe\
+\x16)\xf3\x82P\xac\x98\xcd\xe6\xd1~\x09\x17\xf9\xaa\xac\
+ho)\x01\xa4\x18E:V\x86\xe4\x84a\x1b\xac\x98\
+B\xbaYt\xac\xb0\xe4r@\xbf\x88\x96\x12\x87s\x98\
+\xe9\x8bn\x11\xf6\x89\xa5\xed\xbbe\xf0'\x96\xcc\xef\x96\
+\xd7\xdf+\x0f}\xcd\x91\xf3\xbe\xc9\xa5\xffLB\xff9\
+\x85\xe9\x87\xf4\xd8Y\xf7y\x06\x8f\x9d}\xfa\x0b\x19\x87\
+~\xcey\xfb\xdc\x16m{l\xa7\xbf\xcf\xb2\xf7WV\
+\xfa\x9bl>\x84\x99)a`\xfb\x7fg\xa5\x17\xc78\
+-\x8ev\xfc\xca\xca\xb40>\xd8\xedQ'\xdc_%\
+\xfd\xc7\x81\xa6U\xc2{s\xd4\x7f\x1ch'a\xba\xef\
+\xc7h\xd8\xd1\xa7\xbf\x12\xd5\x7fUj\xfa+T\xfdW\
+\xab\xf6sd\xcc\x1d\xbe)\xf5U\x1c\x19\xf7\xb7\x9a\xfe\
+Cd\xd3\x8a\xc4\x9d\xacf\xff\x0f\x91\xf5\xc2\xf4`\x9e\
+eE\x7f!G\xaf\xea\xb6\x13GV|\x0f\x8e\xa6\x85\
+\xe1vay8\x8e2\x7f!G.\x8b\xb2#G\xf8\
+59\xe2\x03r\xc4\xbf4B\x0f\xdb.\x1c\xa5\xee\xc1\
+\xd1\xb40\xdc.\xcc\x0f\xf8X\x82\xe6\xdf\xd1C\x09\x8d\
+\x90\xf6A\xda\xfak\xf5\xbe\xf9\xf8R\xb9\x04h\x12\xdb\
+\x8c\x18\xc8M\xd5\xeb{\xa5\x84\x06\xa4\x89>s\x02\xd2\
+\x88\xd4\xa3?U\xd4\x81\x84\xb4\x81i\x82\xc6\x03\x143\
+g\xc8\x92\xa8~\xbf\xdfJ\xaf~\x1a\x10R1]\xab\
+\xed@\xeaEs]\x8cC\x1a35\xe1\xe2\x10\xc3\xda\
+\x16D\x06\xc9&T[\x13\x067t\xa9`\x82\x9a\xc5\
+\x11;\xc4\xb0\x97\x9c\xca\xf7\xbbR\x1bX\xfe4(\xc3\
+V+/\x01\x06\xc2L\x97#\x11\x08F\xe3Y\x01U\
+\x0f\xb4\xdc\xc2B{\x89\xb5{#\x9f\x87\xe9\xb0\xed\xef\
+\xb4>\xfe\xde\xc8\xe6;\xad\xcb\xdd\x9bw\x17\x8b\x17\x8b\
+\x9f\x16\x97Wgg\xebo\xb9V-}\xd3\xcd/\xd0\
+\x8d\x98p\xaf\x9b\xf1\xa1%6\xf5$\xd7O.\xc6\xcc\
+\xc5\xf3 \xea\x0a\x86F\x0c\xa0\xb3\xd5\x91\x05\x812\x17\
+\xe2\x86\x11G\xe4Q H:\x96\xd2 \x95\xc2\xe9G\
+^Z-\xae\xdc\x04A6j\x0b8[\xddO\x9c\x91\
+\x83z\xc6\xc3^m\xb6\xfd+\x80mH\xb5A\x07t\
+\xc2\xa8\x0d\x22\xa4\xba{\xdf\x8b\x1f\xbb\xdd\x07\xde\xee\xb6\
+=T\x91B\x85\xa2\x0c\xf3\x8a\xe3^Q #,:\
+c\x10\x13J\xe3\xe7\x9b\xecs\xf0n\x9ea*\xf8y\
+T\x7f\xb7<\xa1\x5c66~E\xf5\xf9\xaf\xac\x02P\
+TEn&\x19E$*\xed\x01\x5cH]f\x05\x8a\
+\x17+Y\xf6WEh\xbaR\x0e\xaa\xa8\xbb\x05\x91J\
+\xce\xd2!-B\xb5\x8auH\x14\x8bG\xaa\x90B\x9f\
+r\x84@%*\x19\xdf=\xa1\x1d>\x90v~\xd7\xe7\
+\xa6Zo\xd1\xf2S\x9dA9]n\x9f\x8ax\x06E\
+\x08\xb9\xb4\x18\x91\xc4Tt\x08y\x04\x81\xec\xa5E3\
+,\x5c\xdc\x86 &@li3\x170rb\xaa\xd8\
+*\xdc\xcd\x5c!\x5c9c\xa8\x9d\x18Yk\x1b\xa4\xa9\
+J\xb6H\x16`,\xadM\x22\x84\xe2:hV\x04\x94\
+L9*\xaaCH,G\x5c@0,u\xc0\x04Q\
+m\xc4\xd6j\xff\xb8\x8e\x06\x94BDC\x84\x1bQ\x03\
+\xf2\x12\xc6-d\x9a\x9aZ4_N\x0b\x13\xde>\xf6\
+m\xe8\xe3\xf0\xa7\xa1\xbd\xd6\x9f\x88ur\x90X\xc7\x91\
+`\x86\xaa3&P\xd7\xa1\xaf\xdc8C\xd3\x98\x85@\
+\xa2\x17\x1b\xc6O\x0cA\xc5+j\xa0\xe4\x864`E\
+\xc9f\xe1\x90\x9c$1TF\x14\x5c\xaej\xe2\x84N\
+\xad\xc9h\xd9\xa1\x00F\xba\xb7\xc6\x92*e\x0c\xee\x98\
+u\xc7\x86\x9d4\xca2\xec\x8dE\x7f\x1c\xf6Y\xa0p\
+a\xe1V\xb5\xed#\x84\xa4X+M\x90\x1a$\xcd\xd9\
+\xd9\x94S\xb6\x0ej\x0b8\xfa\xee\xd4\x87\xe6\x102\x95\
+J\xe1\x990\x83\xa2\xb1\xcd\xe6\x02\xe4\x81\x99\xf6\xfc\xb1\
+\x1a\xe9\xb1\x1a\xd7\x17\xa6\xf8\x84\xd7\xfa\xf3\x8de2\x00\
+#\x02K|\xd1\x0f\x06\xf4\x16\xb4\x19\x06\xe8\xd3AZ\
+\x18\xc4\x5c\xa2R\xaf\xcd[\x15\xb3\xb9\x1e\x82%1W\
+\xd4\x00M\x1cc\xddu\x1c\x98\xac\x88m8Y\x14.\
+\x9d\xe7\xa9\x80\x16N\xe5\x0eu\x87b\xac!U\x90\x09\
+\x18\x12F6\x94\x22\xbc\x96\x0d\x03'v\xd1\x16a\xb8\
+8\x16;*\x02\xe2\xca63\x07\x16\xb1\xe0\x95;R\
+\xba\x0e\xbd\x8c\xe2\xcb\x93\x18Qp-h\xdaPt\x1d\
+\xe2\x08\x93\xba\x0eCD\xe3,[\x87\xfd\xe3Vt\xd3\
+\xbf\x99\x85w=\xf5\xb4r\x10\xff\xee\xbb\xe5.\xcb\xf0\
+H\xa0V\xdc\x07\xb4\xa8\x06Y\xd3[\x90\x09S\x1b;\
+gA\x1b0\x0a\xc5\x94\x86%\x17,\xbc\x0c\x84\x85(\
+\xc4\x9a6\xb1`\x9a6\xcc\xc3E\x07L\xc2\x82\xaca\
+&m\xcc\x0d\x0b\x0eOY\xd66N]\xa1\xe2N\xb1\
+,iE\x06\xc8027*[\x89\xb0eeu%\
+\xd2\x86\x16!v\x1e0\xcc\xa1\x93\x01D\x9aKL\x82\
+\xc2K\xc3\x04M4V\x11\xdcK\x90\x0f(\x99k.\
+KJ\xf0\x00q`\xe4Fe\x09\xd5U\xdd\x8aR\xc3\
+\x8c\x0b\x97\xd2[pH\xf8\x06T4\xa8\xe4C\xe6\xd0\
+\xc2\x03j\x89.\x1b%=%t\xca%\xa8\x00\xa2\xb3\
+\xd1\xa6\xa3\xb08!\xafP)\x1e\xd1\xa1\x82\xa0\xce\x85\
+\xac5\x9b\x94\xc9+?MUod\x18hJ\xb27\
+sO\x0b'i\x98%K\xe6\xba\xa78T\xa8X\xeb\
+Ua`%$\x9e\x99Aj\xaaSm3\x12\xc4\xd0\
+\x9cf\x8ePP\x92\xf4\xc8\x11(\xd2\x88[*7J\
+B?Rjn\x8c\xe2\x83\xeb&\xb35\xcbp\x84\xd0\
+b*\xad\xa4\x17\xb7\xe5\xe9X\x11\x1b0\x02\xe1\x94\xb0\
+&[$8K\xf3hcV\xd4e\x8f\xc4\x8b\x8a\x0e\
+\xa8\xa9\x96\xaa\xf2\x86zI\x13i\xa8\xaa\x9a\x97\x16\x10\
+Z\xd78\x1b&\xaa\xc5}\x15$\x04\xa9\x8b\x12\x0d\x15\
+&\xcb\x01E\xd2hcw\x06\x0cE\xb5\x99(P:\
+\xd2\x93\x11%\x85)\xe8\xfe\xa4\x81\xa9\xb1\x1c\x19\x8c1\
+x\x1fFX\xc5\x18R\x22\x9c\xbbX\xdaP\xc7\x94\x18\
+\xd0,\x91<`\x91\x962`\xa1\x1a\x0d*HE\xbd\
+w\xf2e\xba.\xbcD\x991\x1bf\x82\x1a:`F\
+X|k\x88\xf8q\x1b\xba\xe5|\xc5w\x8fg\xfe-\
+\x9e}\x8bg\xdf\xe2\xd9\xb7x\xf6\x1b\x8eg\xc6\xbas\
+<\xdb\xe7\xfa\xeb\xfb\xe3\xdb\x9f^\xff\xf1w\xff\x1f\x87\
+I\xee\xb2\x11\x94\x00\x00\
+\x00\x00\x19\x91\
+\x1f\
+\x8b\x08\x00\x00\x00\x00\x00\x00\x00\xed}mo\x1c9\x92\
+\xe6\xf7\xf9\x15:\xcf\x971\xae*\x14\xef\x11\xf4t\xcf\
+\x027\x83],\xd0\x8b;\xdc\xce\xe0>.d\xa9d\
+kG\x96\x04In\xdb\xf3\xeb\x97\x99%\x89E)[\
+Y\x96J\xee\xde\xeb.\xb7\xdb\x95O2\x18\x8c\x87\xc1\
+\x88L2\x8b\xf9\xdd?}\xfep\xba\xf7\xe3\xea\xf2\xea\
+\xe4\xfc\xec\xfbW\x04\xf8jouvx~tr\xf6\
+\xee\xfbW\x7f\xfb\xeb?/\xf3\xd5\xde\xd5\xf5\xc1\xd9\xd1\
+\xc1\xe9\xf9\xd9\xea\xfbWg\xe7\xaf\xfe\xe9O\xbf\xfb\xee\
+\x7f,\x97{\x7f\xbe\x5c\x1d\x5c\xaf\x8e\xf6>\x9d\x5c\xbf\
+\xdf\xfb\xd7\xb3\xbf_\x1d\x1e\x5c\xac\xf6\xfe\xf0\xfe\xfa\xfa\
+\xe2\xcd\xfe\xfe\xa7O\x9f\xe0\xe4\x06\x84\xf3\xcbw\xfb\xaf\
+\xf7\x96\xcb*y\xf5\xe3\xbb\xdf\xed\xed\xedU\xb5gW\
+o\x8e\x0e\xbf\x7fuS\xfe\xe2\xe3\xe5\xe9X\xee\xe8p\
+\x7fu\xba\xfa\xb0:\xbb\xbe\xda'\xa0\xfdW\xad\xf8a\
++~8(?\xf9qux\xfe\xe1\xc3\xf9\xd9\xd5(\
+yv\xf5\xfb\x8d\xc2\x97G\xc7\xb5tk\xcc'\x19\x0b\
+Q)e\x1fy\x9fyYK,\xaf\xbe\x9c]\x1f|\
+^\xf6\xa2\xb5\x8dS\xa2\x8c\x88\xfb\xf5\x5c+\xb9]\xa9\
+7\x9fO+\x13?\xd9\x98\xf1\xec\xa6\xf6\xca\xfeE\xfd\
+{'p\x0b\xc0\xd5\xf9\xc7\xcb\xc3\xd5q\x95\x5c\xc1\xd9\
+\xeaz\xff/\x7f\xfd\xcb\xdd\xc9%\xc2\xd1\xf5\xd1F5\
+\xb7\xe4wz\xbb\x1e9;\xf8\xb0\xba\xba88\x5c]\
+\xed\xdf\xe2\x83|\xef\x0e\x15\xa8=|t\xfd\xbe\x1er\
+\x8e\x87\xefW'\xef\xde_\xb7\xe3\x93\xa3\xef_U\x83\
+\xd9\xd1\xc7\xe3\xdb&\xbd\xb9\xab\x08Ax<u\xabg\
+\xf3\x94\xde\x93::?\xac-\xab\xed~w\xbe\xbc\xb8\
+\x5c\xfdxr\xfe\xf1j\xf9\xe3\xc9\xea\x13T%\xff\xe8\
+\xab9\xffx}\xf1\xf1\xfa?V\x9f\xafWg\xeb\xfa\
+\xaaa\xcd\xca\xf5\xe9Q\xee\x0e\xec+X}\xbe8\xbf\
+\xbc^\x1e\x9f\x9c\xae\xd6J\xf7\xdf\x9f\x7fX\xed_\x9c\
+\x9cU+/\xcf\xeb\x97\xc3\xab\xfd\xf3\xcf_\xde\xad\xce\
+\xf6\xab\xc4\xe9\xc1\xdb\xd3\xd5\xfe\xc1\xe1u\xd5u\xb5\xff\
+\xa0\x81\x17g\xef&\xab\xff|tQ\xbb\xd3\x03l\xf2\
+\xf4\x97v\xfaO\xf5\xfcw\x1fV\xd7\x07G\x07\xd7\x07\
+\xf5\xfb\x0d\xbd\xb7\x88\xc6X\xa2\x96\xa9\xce\xfb\xe6\xff\xfe\
+\xe5\x9f\x87\xa3\xf1\xf8\xf0\xf0\xcd\xff;\xbf\xfc\xfbx8\
+~\x86\x02\x07o\xcf?\xd6\x8e\x1aen\xca\x1d\x1d\xbe\
+\xa9\x0e\xf4\xe1\xe0\xfaO'\x1f\x0e\xde\xad\x06O\xfd\x9f\
+\xd5a\xbe\xdbo'\xba\xc2\xd7_.V\x15\xe8\xaa\xbd\
+\x5c\xad=qj\xf0\xd6\xff>\x9c\x0cB\xfb\xff~}\
+rz\xfa\xaf\x83\x92W{\xfbw\xed\xdc\xbfih\x05\
+\xc6\xc3fG=\xb85s<\xbas\x88\xa1c\x8e\x06\
+~+\xba\xc9\xde\xa7\x93\xb3\xa3\xf3O\xcb[w\xf4\xc8\
+W\xd3%n\xfd\x972\xee\x97\xb8\xa8\xcd\xbbz\x7fP\
+K}\xff\x8a\xa7N\x9e\xd7\x01R\xed\x19\x1c\x15o\xce\
+\xbf\xfbxr\xb4\xba>?]]\x1e\x9c\x0d$\xd0\xc6\
+\xa9\xcb\xaaj\xf2\xcc\xf9\xdb\xff\x5c\x1d^O\x9f{{\
+~y\xb4\xba\xbc\xd3D\xf7N\x1c\x9e\x9f\x9e_~\xff\
+\xea\xf7>~nN\x0dm\xbb=q<~^5w\
+y{p\xb5\xba9\xbcz\x7f\xfe\xa9\xb6\xaa\x82\xd7\x97\
+\x1fW\xf7-\xfc\xc7\xf9\xf9\x87Aa\xa2z\x86\xdd?\
+}\xf8\xf9\xfbWKc\x08\xe7\xd0\xf2\xe0lmk\x09\
+ \x8e\x92\xf1\x13\xd4\xd7\x0a\xf0'NUi5\x7fP\
+\xe9\xc7\xcb\xcb\x1a\xfb\x97\xa7\x07_V\x97-\xae\xdc\xb8\
+\xcb]\xb1\xc1\xa4[\xb7\xac\xdeV\xf9\xacc\xb4bC\
+uw4\x0c\x00+\xf3\x1d\xf8\xe3\xc9\xd5I\x1d\xc0\x8d\
+\x8b\xf1SG~\x05\x8f\xee\xa1Cd\xacY\xb0\x1a\xc0\
+0\xf0~\xf1\xf9\xfe\xa9/\x13\xa7V\x1f.n\xce\xd6\
+\x93\xd5\xef\xd7~\xfd\xd0\x95G\xfchu|\xd5:m\
+8b\xc7|`\xebE\x8d\x95\x17\xab\xc3!\xdd\xdd\xa8\
+i\xc1rm{_T\x1a\x0b-\xde^\xfc\xc7\xd0\x15\
+{o\xf6\x5c\xeb\xffh\xb2\xc4\x97u\x09B\x1c\xfe\xc1\
+\xc92\xff\x18\xe3\xfeT=}\x13\x96\xe7\x97'\xefN\
+j@\x1e\xcb\xe9\x9a(\x8f^\xa6Z\xbda\x9c\x96\xbb\
+@\xf1]\xcd\x89\xab\x83\xcb\x7f\xb9<8:\xa9\xde\xb0\
+)\xd0\x9f\x11\xadRw\xc1\xe5\xea\xfa\xfcb\xf8\xdeJ\
+\x0f\x88\xa85\x9d\x95\xbb\xeb/\xa7\xab\xf5\x99\xe58\x82\
+\xde\xfc\x1e\xc7\xcf\x1fG\xe8f\x18\xbe\xd9\x149?>\
+\xbeZ\xd5\x10\x83\xb5\x81\xf3\xca\xe4\xeb\x95\xe1\x842j\
+l\xec\xf7F\x7f-G\x5cl\x96#.\xf1x\xb3]\
+V\xe5\xf8x7\x1cq)\x8f+[G\xb4o\xca\x91\
+\xe3<G\xce\xf3\xcd\xde\x19G\xae\xbf8\x8e\x90\xe79\
+\xc2\x99f\x1f\x1e\xadV;\xe3\x08\xfdqeG\xbc\xca\
+\xd5\xd1\x0e8\xaa\x17\x88\xd7\xab\xcb[\xc1!-\x22\x10\
+2\x06\xd2m\xd6l\xd7\xc9\xc0\xa8\xa4\xd8\xe2\xc0\x97u\
+y\xd6b\x8ew\xc6\xb6\x0bi`-\x85\xb8\x0b\x8ck\
+\x8d\x22\x19\x8d\xf3\xe3\xd5\xbf\x1c|\xbc\xba:98\xfb\
+_\xa7\x1f\xd7\xad\xe9\x83o5\xfbtuX\xab<8\
+\xfdt\xf0\xe5\xaa#\xe7\xe8/\xf5R\xf5\xe0\xfa\xf6\xaa\
+[\xdd\xcc\x0b\xbd\xeai\xed5\x88d\xd9`f\xdd\xa4\
+\xaf\xf4\x1aW\xd49\xafq\x9d\xebHLZ\xc9j'\
+^\xe3\x8a\xf9\xb82\xc98Z\xe9\x8c\xb2]\x8e,\xaf\
+\x9fY\x8e\xdcg\x9a]\x8e\x0f\xe9\x98w3\xb2(g\
+\xb2\x98e\xac\xde\xda\x16\xca\xc0\xe6\xd5\xb9\x07\xce\xf4\xff\
+!\x1fXN\xa8{\xc6@\xee\x07\x1a\xbd\xd0@37\
+\xb6\xe4||\xa0\xd1\xd7\x0e\xb4\xcf\xf4\xfd\xab\x10\x08e\
+\x97l\x81\xa6\xa2d \xe2\xa6-\x0e\x7f\xe6Z6\xa1\
+H\x11)\xadlE\xc9!\x0c\xc3\x1a\xfa\xd09M\xa5\
+u\xce8Y\xf1\xe6\xfd\xe5\xea\xb8\xdew<H\xa1\xad\
+\xdc\xbb\x1b\xf0og'\xd7u\x9a\xe4\xe3\xd5\xea\xf2\xdf\
+\x87\xa9\x86\xff}\xf6\xb7\xab\xcdk\xec:\x8ds\xf4o\
+\xab\xeb\xf7\xe7Um\xadt\xe0\xb4\xf20o\xb9BX\
+o\xb5\x02\x87e\xee\xd0j\xde\xce\xeab\xdf\xcaj\x8a\
+\x02\x14\xcc\xda[\xce\x02a\xe9\xbd\xe5\x14\x02%1\xa8\
+\xb3\xdc\x0dT\x93\x88\x1f\xb7\xdcwny_\xea\xaf\xf5\
+\x1e\xf8j\x98m\xa8\xf7\x5c\xc3\xd7\xd3:\x91\xf8\x87\x12\
+\x0b|\xbd\x15\x0d\xc5A=\xd5;\x1a\x5c\x80$\x8d\xac\
+\xa3\xc1\x18\x98R={\x1a\x1c\x98U-\x1e\xa1\x81\x8b\
+\xc9\xae\xdc~\x9e\x86:\xefry\xf2\xf9\x0f\x04\xc8^\
+\x1c}\x81\xf5O;Z\x8a\x03\xa3F\x94\xc5\x92 \xc5\
+<\xcc\xb7\x22+c\xcdU\x1f#\x96\x08\xe2\xa4\xc9\xe6\
+\xbd\xd7\x90CPj\xf4\xe3\x05\x1d\x94\xc3\xfcq\xba\xfc\
+\xff\x03\xba4\xa0xjp?\xc4|\xed[\xd1\x91\xa5\
+\x05\x98\xfa\xb2\x15M\x1b\xb9\xb2\xf28Ye;\xb2\x90\
+\xbf\x09Y\x86\xf5#\x03Y^DYh\x83\xac\xa1\xc2\
+\x83\xd3\xfbd\x0dSC\xb4a\xfa0\x19\x14\xcd\x03\xea\
+\xd4\x8d\xb5\xa3\xe3\xa1p&dRHC\xab\x081\x81\
+*\x93tl\xf5*\xb9\xf8V#\xd1\xebggl-\
+\x1f\xd0\x85n\xee\x0bF\x07\x09\xa7\xa8d1\xa8{\xf1\
+\xdc\x8a\xac%9\xb8\xa7fv\x9c\xa5C\x0ezl\x93\
+:\xc7F\xd2\xb4\xe8q'\xfa(w\xb9\x95\xa7\xa9\x96\
+\xddq\x87\xc0\x96\xeeXIB0&\xd6\xd0\xe1kr\
+\xa8\xa8.\x10\x94\xd0\x91hA\x82\xc0R\xd4\x16\x86\x80\
+fVl\xcb\x1c\xa0`\x0f\xd2`AO\xe9\x03\x1a*\
+\xb0u\x03\xd4\x80\x065\xc9\x8f\x0e\xd0\xa0\xad\x5cNQ\
+w\xe8r\xa3\xab!\xa4\x13\xaa\xcb\x82\x91!\x86A\xa9\
+\xe2\x94\x1e\xb3\xcc\x8c\xc1:\xfa\x90\xbed\xf1\x9e(\xd4\
+V\x80Z\x81\xf9\x0c\x8d\x8be:\xb0p\x8a\xbe\xde\xce\
+\xecG8\x16\xdc\xd25\x91k_\xcc\x5c\x80\xcf\x8d\xc0\
+:\xa8\xc4@\x89\xb3\x94n\x10\x85CA\xf3\x22\xfd\x88\
++@2f\x90n\xb0\xba\xbe\xea\x864\xe3v\xc4-\
+\x89\x0b\x0cZ\xea(`\xb04.\xa2\xab\xa5?\x85\xc4\
+\xde>\xc1\xed2\x09\x97\x82_A\xe2\xe1\xe9\xc9\xc5\xff\
+9\xb8~\xbf\xa9\xf8\x16\x13\xb46\xa0n\xc1\xc9v\xb7\
+\xfb\xa9\x8bVW\xbb\xbf\xbb\xbb\x8f\xfb\xe3q])z\
+S\xd7\x90\xfe\xf0\xfb\x87\xd6\xbd\x1e\xcf.[\xe1\xab\xeb\
+\xcb\xf3\xbf\xaf\xde\x9c\xd5\xf5\xe8\x9b\xef\xeb\x05\x9e7\x08\
+$\xa1\xa5~n\xf1\x81\x83j\xe8\x9b\xcb\xf3\x8fgG\
+\x9b\xe0\x7f\x9e\x9f\x9c\xf5h]\xb4Z]\x9e\x9e\xd4\x7f\
+\xde\xe8-vtP\x97\x86./\x0f\xbet\xca\x06t\
+\xbc\xdd\xac%\x81\xec\x06\x9e\xbc+\xad\xac\xfd\xdb\xde\x92\
+\x98\x80\x13\xcb\x82\x0b\x98\xa1+\xef\xfdy@\x19\xcc\xcd\
+y\xc1\x09\xecVx\xc0\x14\x5c\xdc\xa2\xc3\x0c\x0a\x13o\
+\x8a\xffPa\xbd\xf5(c\xf0t.k8\x18\xa8\xb0\
+\xddW\x16\x02\x1a!\xb4Yq\x18\x98&{\x879\xa4\
+p\xc9{\xcaR\xc0\xd0e!\x0e\xccR|]i*\
+D!\xe4\x85\x04\x18\xa6\xa7t`\x01\x8b\xe2\x5c6\xe4\
+\x15!]\xc5\xc6J\x1dA\x02\xdd\x16\xae\x9d\x92\x0c \
+1\xb7^G&(\xa3\x04m\x82\x05A\x0b\x09\xc5\x86\
+x!\x88p\x94\x1c*m\x06\x95*\xafE,\x87j\
+\x9b\xed\xa5@\x88bnp\xd4\xb0Fg\x13\xef\xc9\x0f\
+\x03!\x09\x1c\xe0\xd6U\x9d\xb2\xd6\xab\xad\xe2\xd6\xfb\x0d\
+k~\xd2\xeb\xa2*\x8da\xd9\x0c\x1b+%\x01aN\
+o\x1cl\x82\x8d\xadVA#v\xa8V\x02B\x85\xa5\
+\xb2\xdf\xa9i}\xd4ii\xbd\xd9\xc0\xd6\xef\x9b\xf2\xcd\
+G~\x98\xf4\xfd\x7f\xdc\x9b\x09\xb9X\xc7\x94\xd8\x98\xff\
+\xb8\x8d*\xbf\xe4\x90\xceL\xa0\xa2\xf6\x12!]\xbey\
+H\xd7\xf2\x8c\x90\xdew\xe4\xc3\x00\xc8$\xe0\x96\xd6\xc7\
+$&\x85\xa2\xc2\x9b\x01\x90)\x00I):,\x81K\
+\xe6\xbd\x00\xc8z\xc3\x7f\x1f\x00\xd9\x15,\x90\xca=e\
+n\x90\xa6\x856+\xf6\x80\xc2\x96\xdea\x05\x98\xd0\xf2\
+\x9e\xb2\xa8\xe2)\xa5\x0f\x80\x1c\x01\xe4\xe9\x9b\x01\xb0\x81\
+m\xc84\xf9.\x00\xb21\x84&\xaf\x03`+\xd4\xc6\
+i\xa7\xa3\x0d\xe9\x06\xb6\xc1\xdf\xc4\xbb\x00\xd8\x0c\xeab\
+R\xb3\xbd\xc5\x9f\xc6Q\xc3\x1a\x9dM\xbc#\xbf\x0b\x80\
+\xad\xab:e\xadW[\xc5\xad\xf7\x1b\xd6\xfc\xa4\xd7\x85\
+\x0e%\x8d\xfa\x00Xa\x83@\xd4.\x006\xb0\xb1\xd5\
+*\xe8\x02 \x0b\x021\xe6\x10\x00;5\xad\x8f:-\
+\xad7\x1b\xd8\xfa\xbd\xc9w\x01\xb0\xd9\xd4\x05\xc0\xa7]\
+\x0a\xc9\x7f\xbbK\xa1\x9f\x0e\xe9}({\xb1\xbb\x8f]\
+\xdd\x18M\xdf\xbfp\xfc\xfa\xee_\x0a\x01R!\xe4]\
+$:\xca]'\xba_>\x81\x99\xbb$\xd0\x7f\x1d\x04\
+\xb6\xcb\xad\x1d\x13\x88\xe5\xd7\xe7\x81EwI`\xfc\x0a\
+=\xd0vI mK\xe0oIdr\x82\xbb\xec4\
+\x89\xfc*o9\xb9\x14\xde\x96\xc4\xdf\xa6b\x7f\x92\xc4\
+|yO\xdc\xe1z\xfbR\x11\xc24Y\xa6\x99x\xf9\
+\x9e\x9a&Q\xbf\xa5'\xaa\x80\x86\x86sg\x1f\xa1\x01\
+\x87*w\xf6\xb9C&%Zg^\x12\x08[\x84u\
+V\xba\xce<\xee1\xbf\x1c\x84\x10\xc4\xa29,\x9c\x11\
+;Z\xd2\x02\x81D-X\xeb\xb7pT\xa7X\x04\xa8\
+\x9b1\xda\x82\x11(\x94d\x17aU\xcao\xfd\xf0u\
+\xfd\xb0$D0Q\xcd]v\x84\x10\xe5\xb7\x9e\x0d$\
+\xd2\x9d,\xf0\xf4s\x19\xbdM\xdb\xcfe\x90\x83\x16C\
+\xc4\xd4_\xd6\xba\x8e\x17\xc0\x22a\x0b6p)\xb8\x9e\
+i\x0c\x02\x96\xd4RF\xb8`\xc9\xac\xa0\x00G:/\
+\xd8\xc1\xc2M\xacb\x0a\x91\xc4U<\x810\xa2\x8c\xe2\
+\x85 \xd0\x8a\xe4B\x0dPX\xc3\x1aX\x16\xc1\x10j\
+\x8eeC>\x0bx \x17\x1f\xe4- \xcd-xA\
+\xe8`EK-*\x08\xa4\xa2R\xee\xc0\xda&\x12`\
+\xca\xe8\xe5e\xad\xe8NO\xde |\xd7\x1co\x92}\
+\xcb\xc9A\xd5\xc9u\xc1\x0a\x99\x051\x86\xe9/ps\
+&\x1b\xc0B\xe4\x9c\x15T\xb8\x95\xbe\xa3\x8ds$3\
+m!\x04\xe2\xae\xe6#H\xc8J\xba\x905o\x18w\
+\xd2R \x8a\xf1\x8dn\x05v\x22\xd1\x85!(\x16r\
+k -<*\x88\xe4Mw$ )\xea\xc8\x99\x18\
+h\xd6\xcfxo\xe9\x1c\x85\xabnc0\x22\x92[\x8c\
+*\xe6\x02\xc4\xf7\x84C $3\xca\x9d\x96h`\xde\
+\xb5'n\xa5\xfb\x86;\x82\x17\xb2B\x0b\xa9\xd2\xecQ\
+\xa8\xaa.P\x9c\x8a\xf1B\x04\xa2\x14\xc1A\x9a\x80s\
+\x94\xaf\x22\x81\xb7\xf2\x0c\x8ehC\xcc\x090%\x0e\xaf\
+\xa0\x81\x97B\x12\x83\x0b\x9a!\x9bNz\xeb?\xf6\xaa\
+\x17\xab\x8e\xadb\x07\xb1\xdb\xae\xd4\x046M\x8e\x0a\x8f\
+\xad\x22\xa9\xad\x220\xa3\xc8\x0a\x16H&u[\x83C\
++\x8b\x91\x8dS\xe5k\xc4\xed\x16\xf9\xf3\x1a\x91\xe4j\
+\x8e'\xb8\xb9\x10U\x10\x81(4i\x11\x04\x94\x14\xaa\
+Uo\xdc\xb8t0\x98\xder\xa4\x06\xaa\x86\xbc\x08\x81\
+\x82\x99\x92\x15c0\xd3\x92w\xe0 \x8dk\xb7\xec\xa5\
+% \x93\x8d\xe2N\x11W\xd0A}P\xbf\xd9$\xf1\
+\xae\xe5?\xdc \x22\xcd\x96Q0\xd4R\x067-\xac\
+\xca1\xaa\xe0d\xa1\x81\x98\x12\x96j\x15K\x88\x9b1\
+\xb2i\x0a\x02\x99Q\xe1\xa1\xbb\x884nl\xc1\x92\x88\
+>v\x02+\x1aMt\xcb\xe4\xb2\x19\x91O\xcc\xb1\xfe\
+*\x92m\x09p\xf7\xa4\x1d'[\xfe\xe6\xc9\x16sG\
+KoD\xf8 G\x91;0;f\x9f\xa4\xc8\x13$\
+0y3IQ (a\xb1\xcd,EAPH,\
+\xef\x05\xfbLHI\xf6\x96\xa66\xc1\x96\xa6Z\x05}\
+\x9e\x22S(\x99e3M\x11\x07pXx\x97\xa6\x08\
+A\x8c\xf0\x9e|\x11Pv)\xd1R\xd5-\xa8\x18-\
+[\xb5\x0a\xee[@\x02\x16\xa9e3]\x11%D\xa6\
+\xe9f\xba\x22f@\x0c\x91>c\x11\xdb@-Q\xcb\
+X7\xa0ht\x19\xabU\xd0'-\xaarE\xd4[\
+\xd2\xda\x04[\xd2j\x15\xf4\xa9\x87\x84\xc1\x99\x986\xf3\
+\x16i\x01WQ\xdfL\x5c\xe4\x08l\x0f+\x08\x84\x0c\
+\xe2\x96\xba\x1a\xd62W\x13\xbfo\x80\x05$K\xe1\x96\
+\xbc\xd6 \x16\xe5\xdcL^d\x09Jb\xd9g/\xb2\
+\x02!\x9c\xb1\x99\xbd\xc8\x19RH\xbde\xaf\xce\x8f\xfb\
+\xf4EJ\xeb\xa6\xf5\x19\x8c\xd4@2\xc863\x18i\
+\x82\xa7\x12\xb5\x0cv\x03\x0a\x8bti\xac\xc1}.\x1b\
+\xe1@\xeb\x12\x07i\x00k\xe9s\x19\xa9\xae\xfd\xbeO\
+G\xa4\xf5(Bl3\x9b\x91\x14\xf0\x0c\xa1\x96\xcd*\
+\x167n\xdb\xcb\x8b\x01\x92\xa6n\xa63\x12\x01+\x8a\
+\xd1\xb5J\xa4Y\xd0\x0ckp\x97\xd8F\x18\xbd\xd0f\
+b#QPR\xd7\x96\xd9\xd6-\xc8\x9b\xf1\xd45,\
+\x803\xacKm\x83a,(\xd2R[\xd7e]v\
+\xdb\xfe\xee\x81\xff\xfb\xdd=L\xa4\xe9\xdf\xee\x89\x9f>\
+i\x8e\xbbL\xd3s?\x0d\xe3\xa4\xd9jf~;6\
+\xfd\xab/Ny\xe4\x97c\xc2\x19\x9c\x91\xad\xc8\x8c\x11\
+\xf3\xbf\x1dk\xab\xe1L\x80B\xdc\xffZF\x08$\xdc\
+#\xfb_\xd6 \xb8k\x1aw\xab\xe3\x85!+(\xba\
+\xe5\xb3\xc3\xc0\x22\xcc\x5c\x16\x04\x85\x0c\x8b\xd2\x0e\x1e!\
+f\xb6\xed\x1e\xd3V\xdf\xc6\x11fYS\x07\xe6L+\
+\x1dmV*j\x89\xd1\xd1\x16\x90%\x84):\xdaZ\
+\xd9mis\x16\xaf\xb4\xed\x84.,\xdf\x94.\xa2\x00\
+a\xf1\xdc\x82.F\xe0\x94\x0c\x9a\xa6\xeb\xd9\xc6\xbb[\
+le\xbc\x13>\xcf\xf8\xf9\x07Y\x08\xdc\xc8\x89K\xed\
+\xd3_.]\xb63\xba\xe6c\xac\xab\xe5\x8b\xc4XW\
+\xc7Gb,\xa7\xaaR\xc63bl\x9f\x9b\xb6\xf9\x1d\
+V\x14#\xa4\xf5\xaf=\xda\xd1\xb2\x80\x858\xb9\xd7\xaf\
+\x02E\xc2X^\xcf\xa4\xeam\xbb\xb9]T0\x98\x15\
+M\xef/*(\x81\xd4$\xfb\x9fl1\xa0b\x98\xb4\
+\xcb\x87\xc9\xb2\x87\x13e\xa7\xf2vq\xdbj\xb1\xbb\xb8\
+\xca3\x86`S?Q\xed\xcc/\xd1\x8b\xabM\xfd\x8a\
+}n+\x8e\x89\xfdc\xfeX[8\xaf-&\xb4\xd1\
+\xe3\xda\x9c\xa7\xb4\xedj/\x02\xb6\xc89\x96\xd8\x12\xbf\
+\x9e\xa5\x88\xa7\xb1\xc4\x96\xfc\xf5,\x1d\xd2K\xb2\xe4\xaa\
+>\xbf\xab\x85\xe6\x13|i\xfc|=K\xae\x863,\
+m\xa9\x0dw\xc1\xd2\xf6\xc9a\x96F'~\x0a\x8do\
+\x8f\x13\x9f@\xa3\x93\xce\xd08\xabm\xf74\x0a\xcd\xb3\
+$\xf2\x84!\x89\x12\xf8$\x96\xc4\xbe\x9e\xa5\xb7\xc7\xb6\
+\xc2\x17di>\xbc\xfb\x13\xc3\xbb\x9b\xdbSX\xd2x\
+\x8a/\x1dUoz)\x96\xd8\x1c{\x96f\x1ac\xe5\
+\xa1\x83\xccP\xd8\x02\xb7s\xc7\xd2\x9c\xb6<B\xdcJ\
+\x1bMk\xd3\xdd\xf9R\xe8\xbc/\x85\x7f\xbd/\xbd}\
+\xe2\xa5\x82{\xe4S|\x89\xea\xe7\xa5|IXb~\
+C0)O\x19q\xab\xb7\xf9\xf6\xebY\x12V\xfa\xa5\
+%A.\x05\xe7X\xe2R\x9e\x90\xe3\x0e\xc7\xcf\xd7\xb3\
+\xc4\xa5<%\xc7\xd1\xf0\xe7\xa5.\xa8\x04\x99f}\x09\
+\xf9\x099\x0eq\x9b\x11\xf7\xd5U \x90\xb9\xb8\xb8N\
+\x86C\xb0W\x93\x06\x94-\x9c\x18\x85&\xabtfW\
+\xaa<=\xa1\xb1\x9e\xc1\xc1!\xdb\x0c\x22\xe4\x99\xe4\xbe\
+\xbdZ\xf1\xf1O\xcc\xf9\xc9\xf4n\xa7\xd3\xfb\x84\xb2\xa9\
+<o\xdf\xd1\xf9\xfdL\xe7\xf7E\x9d\xdf]u~\x9f\
+\xd6\x99{\xd8\xb9\xed\xc6\xe6'\xf1\x1e\x89J\x1e\xb4\xfd\
+\xec\xc1\xf4\x04S\xdbNH\x08\x828\xd4\xe7f\xae*\
+Z\x12\xc8\x83\xc2\xb6\xda\xb3\x86\x5c\xcdB\xee\xf6\xac)\
+N\xc3\xba\x829\x14B\xe3\xfa\x95 \xc3J`y\xfd\
+\xb3\xd2\x99\xb1\xcb\xcd\xb0R@\x89\xcb\x02\xc1\x15\xd3l\
+~\x1e\xa8\xf5\x11\x13`\xd1R\xb4\xd1\xbeF\x09\xa9\xa2\
+]\x17\x91\x01e!\x91\xae\x8b8\x001\xd2t\xe2\xc9\
+\xcd\xdd1Z?\x8f=\x81\xea\x05w\xb9#\x8d\x9b\x87\
+\x88\xdd8\x12\xb3rY?'\xe0.Y\x16KQ\xa0\
+\xc4(?\xc5\xf4\xc5\xc1Q7\xe1\xa5\x0c\x81(\xc6\xfd\
+\xe4\x18\x0aXb\xa3\xf3x\xb2\xe8qWtbv\xee\
+e]\x19\xf5QW.\xfe\x84\xc8 \x05B\xa9\xdc\xff\
+M%\x09\x98\xa6\x94V\x96'\xcbVtiPX\x8c\
+3\xb6}\x80\xddA\x17\x0cd\xab%\xbf\xe8\xe0\x17.\
+\xf6\x18c\x81\xf2\x04\xc6\x96\x8eP\xd4\xd8\xa8\xa7\x0c\x0d\
+R\xd0\xfa\x81\xba\xb4\x02J\xc6j\x1dgj\xe0B\x8c\
+\xf1s\xc6\xbe@\xdb\xce\xfcg\xacy}\xa6\xd9u\xb6\
+\xf9\xdc\xd3\xd6\xef~^\xba\x9e\x9f*\xe6WV\x1bm\
+D\x06\x0fWu\x83\x00S\x0b\xe6\xcc\x0ap\xb7X\xfc\
+\xb3\xb2Vv\xcb\x9a\x12\x17\x14\x1eR,\x151\xcd\x17\
+\xcb\xb1\x02\xc5\x09\xb5\xa74\x81\xc5\x8a\xfe\xbc\x03\x97\xe8\
+7Nw\xce\xa9\xfc\x828\xb5\x02H\xee%\xeeo\x11\
+\xec\xa8(\xd4q\xea\x02jj\xd8ge\x22\x10+\xa4\
+\x13\x83\x7f\xdb\x15\xcf\xfb\xfb\xda-3\xbb]\xed\xb6\xa2\
+k\x07W6*\xac3[\x01\xb6=\x03g6\x17l\
+\x9b\x10VZ\x9e\xb6\x10<\xb9K\xe2\xd4&\x89K2\
+\xb0~\x93\xc4\x1d\x92&\xaa\xe5\x91\xebp\x15\xa6\x99m\
+'\xfb=*\xfb\x8d,g6\xbb<\xecDg\x5cl\
+\xda\xdf\xb7\x1bG\x5c\x09\xdd%i\xec\xf8\xa8\xa7Q\xce\
+l9\xddmO=\xb3\x81w\xb7\xd9\xf7\x9c\xc3\xcd\x8f\
+\xc3\x92H\x8a\xe5f\x1c\x22h\xc1\xa0]\x913\x7fg\
+\xa7B\xdayT1\x084B\xea<\x8a\x12A\x84T\
+\x7fbS\xd9\xc3I\xc9\xc3N\xf29\x8f\xe90\xee\xd8\
+c\x90\x1f\xf7\x18\xbc\xbf\x8f\xb0\x07%\xf5y/\x13B\
+D\xcb\x83\xed\x89M9{\x87I\x07\xd3\xa0|\x1e\x09\
+\x04\xc9\xee\x14\x0b\x01\xd1t\xe1\xfc\x96\xa3\x08\xa3\xe3\xc4\
+\x09(\x1d\xdd{N\x18\xd8\x8a\x92}+R\x96\x05\xa1\
+X1[,c\xf8&\x5c\xe4\x9b\xb2\xa2\xbd\xa7\x04\x90\
+b\x14\xe9X\x19\x93\x13\x86\xddc\xc5\x14\xd2\xcd\xa2c\
+\x85%\xd7\x06=\x8b\x96\x12\xbb\x1b0\xf37\xdd\x22\xec\
+3{\xdbw\xfb\xe0\xcf\xec\x99\xdf\xed\xaf\xff\xcc<\xf4\
+\xf2\x96\xf3S\x93K\xff\x9e\x84\xfe}\x0a\xf3O\xe9\xb1\
+\xb3>\xe5!<v\xf6\xf9Wd\xec\xfaA\xe7\xe9\xc9\
+-\x9azn\xa7_hy\xd6kV\xda*\x9b\x8fa\
+fN\x19\xd8\xf3^\xb4\xd2\xd41\xce\xab\xa3g\xbcf\
+\xa5W\xc6;[\x1fu\xc2\xe7uI{;\xd0|\x97\
+\xf0s9jo\x07\x9aW\xa6O}\x1b\x0d;\xfa\xfc\
+k\xa2\xfa\xd7J\xcd\xbf\x86\xaa\x7fm\xd5\xd3\x062\xe6\
+v/\x95z\xf9\x81\x8c\xcf\xf3\x9a\xf6&\xb2\xf9\x8e\xc4\
+gy\xcd\xfc\x9b\xc8ze\xba\xb3\x91eE\x9f\xc9\xd1\
+q\xfdl\xc5\x91\x15\x7f\x02G\xf3\xcapZY\xee\x8e\
+\xa3\xccgr\xe4\xb2*[r\x84\xdf\x92#\xde!G\
+\xfc\xdc\x08=~\xb6\xe1(\xf5\x99\x1c5e\xf3\x1c\xa5\
+\xef\xf0\xb9\x04\xcd\xdf\x9eJx\xe6\xbd\x0d{\xd8N_\
+x#\xc2\xac\xe3\xb4G;Z\x122\xa0E\xf1\xc52\
+\x14L\xc2\xc5^O\xdd\x10\xcd\xdd<\xf57Z\xd37\
+e?/\x9b\xf9\xe2l\x92\x80X\xbaK\xc5\xb4\x04\x15\
+\xa3\xd7\x93\xb7\xdc3w\xe7\xdd\x9d\xfc\xf4]\xff\xcbR\
+\x89\xfc(\x95I/O%\x81fQ\x1d\x5c\x14\x0a\x17\
+\x13\x7f\x06\x97\xfdLQ?\xa9\xf4\xf3>\xd8\x91/8\
+\xc6Q\x10\x91\x17\xcbR\x00\xb9Hz\xe5\x92\xc1]\x0b\
+\xf9\xeb\xe9Y\xc2\x86\xce\xcfEN\xcf[\xf63\x94?\
+\xeb\x88\xcf\x9d\xae\x92\x92A)\xe18\xbe\xbdI\xa5\xa0\
+\x95\xb9\xd5\xa7\x17zA\xe5\x8b9\xec\xfc\x0a\x08{\xe1\
+-I\xdd\xe5\xa2\x0f\x0b\x98R\xaa/\xb4\x80\x93\x9b4\
+\x07\xde\xf6\x8db\xdb\xbfC\xac_\xb5\xf9y\x1f?z\
+rx\x98_y,\x0e\x88,\xc5o\x9e\x1bd\xb5\xa8\
+\xac\xee\xf8\xcdb?\xe7\xb3H^b\xf7o\xe5|\xa9\
+\xb7\x90\xaey\xfan\xffhu|5~\x1b\xb76j\
+\xfb\x17\xa5\x01\x93\x8b\x8d\xb91\xd8%\xa3\xeeaB\x01\
+\xa6\xc5T\x17\x8e\x10\x98\xc1\xc3\xbb\x1a\xc8\xc1\xdc\x19+\
+J\xa0$\xa5\xc8^\x8b_\xce\xa0\x96%c\x13\x93*\
+\x81Ei\x90n\xa8\x82;g\xb1\xcd\x1a\x0d\x02#\xb3\
+l\xea\xf6A\x0b\xd9\xd0\xa2\xac\xd2(9\xb6\xd3@\xd2\
+\xc7\x16\x15\x04T/1\xa0\x09H\xee\xbaWn\xf5\x8c\
+\x98d\x11\xef0^7\xa9\xd6\xb9\x81\xda\x0d\xf8\xe7\xae\
+(\xb8\x97D\xdd\xcb\x84\x12\x81\xa9\x8b\x02\x88\xe1\xea9\
+I\x5c\xddc\xa5\xdb0\xaa\xa2\xa5B\x93[\xae<\x1c\
+\x93\x0f\xb6\x5c\x19\x0f/?\x9e\xae\xde\xac~\x5c\x9d\x9d\
+\x1f\x1dmn\xc2R{\xf6\xb7\xfe\xfc\xe6\xfd\xa9\xd4\xf7\
+g{\x0e\x9fM=\xc9u\xe2\xfd\x22-\xc9\xed\xa6\x8b\
+\x13\xf4\xc6\x22\x04g\xd1\xb1;2@\x19\x8d\xd6hf\
+0\xed\xa5\x83\xa4\xc4\xdavDd\x8ej\xe7\x9d4\xdd\
+v\xe6\x0f\x83t\x06\x8fh\x01J\xabhO\xa8\x03r\
+\xf1B\x934S\xcf3\x83+[\x96\xbd\xc2\x80\x96\x9e\
+c\x9b\x14\xc3\x91&[\xff\xc3\x14:\xc5\xbel;\x9a\
+\xb2|\x1d\xd5\x7f\x5cO\x91\xae+[\x7f\x17\xc9x\xfd\
+\x13]\x10\x0c6r\xbav\xdb P\xe6B<`\xc4\
+\x11\xb9\x17\x08\x92\x8e\xa5\x0c\x90J\xe1\xf4=/\x83\x14\
+WscL\x8d\x03\xd7\xce6\x98\x8f\x03\xc3\xea\x19w\
+G\xb5\xda\xe1_\x01Lr\xad\x15:\xa0\x13F\x19\x18\
+Ju\xf7\xbe\x15?t\x87w\xe4]O\x5c\x1f\xa5\x90\
++\xf1\x98\xb9\x97\xe3V]\xea.c\xcf%\x88\x1a\x96\
+\xd7\x0f\xa9\xb7\x9fr\xfc@%*\x19\x1b\x8e\xffp5\
+\xedE{#\x1d\xc2\x8c}\xed\x90\xa4\xe8^\xe9`\x02\
+&w\xb4!\xba\xb9`\xa6\x0cQ\xab@\x94\xd4\xac\xa8\
+\x00j\x96\x12{\x8c\x80l\x9a9D2.\x1c\xca\x83\
+4\x11E\xf8\x10\xc9$\x0a\x0a\xad\xa3\x16y\xfa\xa0\x89\
+\x10\x84s\x0c1Y\x80\x9d\x8a\x0d\xa8\x80\xa6\xa1\xed\x15\
+\x011c]cn\xa2\xd4\xb0\x0d\xe9\x1f6\xd1\x00O\
+\xe3\x18\x87SC\x152\xa4\x0c\xb1\x918s@\x04\xcc\
+]\xb8LY>5lbb\xd8LF\xa8\xdcM\x12\
+\xe2H0C\xd5\x05\x13\xa8\xebh'\x93\x02\xa1i,\
+B \xd1\x8b\x95j'\x13CP\xf1\x8a\x1a(\xb9!\
+\x8dXQ\xb2E8$'I\x8c\xc2\x88\x82\xeb\xed\xf5\
+\x9c\xd0i\xa82bA\x5c\x00#\xdd\x87\xca\x92*9\
+\x0c\xee\x98\xf5\xc0\xc6\x834\xca2\x1e\xb5\xa2?\x8c\xc7\
+,P\xb8\xb0p\x15\x1d\x8f\x11BRl(M\x90\x1a\
+$\xc3\x10eSN\x994j\x02l#o\xe6V\x1a\
+!\x93\xd0=\xeb\x18,\x09Z\xca0-!\xe0\xc8\xae\
+\xa6\x13Cp\xe2Zbs\x87\xb4\xe9HX\xe2\xf5\xbd\
+\xfd\xda\x00#\x02K\xbc\xe8\x9b\xab\x9a\x07M\x0dfz\
+d0\x17\x88\x10\xa7\xd1\xf1\x1d\xc5(\x87!\x96\x90\xca\
+\x98k\x94KJ\x8e9\x8b)m\xc4\xb2 \x9b\x8f\x03\
+\xc2os\x93\x96,l\xb7\xa1\x80\x10c\x0c\x05\xaa\x12\
+|\x1b\x0a83\xc7P\xe0\xacY\xd6\xa1 #u\x1d\
+\x0a\x8ac\xacC\x81a\x8c\x91 -\xd7\x1d\xcf`\xa6\
+D9^\xd5`a\x8aZ\xa58\xa4\x92\x86,\xbc@\
+h\x18\xe6\x9e1\x84\x8f\xf7FA\xe0L\xe1\xebL\xe0\
+\x5c\x8a6\xacJG@\x88\x16\xcd\x0d4\x0d<]q\
+,IQ\x10\xcbFl\x08\x04Qv\x92.\x8e\xa8\x00\
+\x16w\x93\x07\xd1%D\xd3\xfa\xe8b\x10\x8a\xee\xb1W\
+\x08\x92\xb9\xc4H\xa7\xa99O\xf5\xc5\xc3\xf0\xa2\x8c[\
+gezl[\xc1m\x1c\xc2\x82L\xca}\x87\xb0\x12\
+iv\xcf!,9\xf2\x9eC\xb8\x17\xf5\x07\x0eA,\
+c\x1cg\x04\xc1\x22\xa5,\x9c\xc1H\x07\x00\xd3\xad\xd0\
+\xe0!\xa2\x12\x98c!2\xc7\x8a)\x10z(\xafE\
+U\xdc\xc8\x87\x92\xae\xea6J\x07*\x8a\x0c\x18){\
+\xe1M\xc5\x0c.\x16T\x9b\xb3\xd9H\x07-\xc8>\x9a\
+\xd8\xcc1\xc8$oF\x0f\x883Qd\xa3g\xc0\xac\
+h:U\xd9\xd6\x99\x03*^\x9c7;\xdd!\x84\x91\
+6\xa1\x04wM.\x0f|F\xc9\xd0\x1f\xf8\x0c\xa6\xe7\
+}\x9f!q\xa5\xa9\xfez\xe04\x82\xea\xdb:M\xe4\
+\x13\x9c\xa6\x10H\xb0\xd9\xdas\xc5\xb4\xf8\x9a\x95\x12%\
+\xd6\x8c\x92\x07\xa5\xef\x15\x06\x11*4bE4T6\
+1\x87\x0cE\x1a\xa2HC\x95\x81KP\xf2v\x03\xac\
+0\xa9\x8dd\x93\xa8\xae]6\x0aI\xe1\xc9v\xfe0\
+\x85\x8e{\xb12C\x86\x14\x1c/J\xb2\x84\xd9\xe8\xcc\
+\x02\x16\xa1J}(\x0aH\xd7(](\x12\x88\x94H\
+\xdb\x080\x81`\xe9B\xde\x87\xa2\x04CB\xe2\x0d4\
+\x1d0L}\x1d\x8a\x0a\xabQ\x1f\x8a\x14\xdd\xa2\xa3\xc4\
+\x0b(\x19\xf3\xd0\xa04\x90 \xb6\xb1\xa8\xb0\x15.C\
+\xc4\xc3\x08\xcc\x1c\xb0d\xe6b{\x9e`R<\xa8a\
+U\xda\x18\xd8\xccs\x13\x15\x87\xa4`-\x83\x1eT\xcd\
+\xe0i\x8a\xa6\xd0\x09\x9f4\xdd\xda'm\xd6'\xbf\xdb\
+\xbf\xfa\xf1\xdd\x9f~\xf7_\xea_?\x7f\x00\x9a\x00\x00\
+\
+\x00\x00\x15,\
+\x1f\
+\x8b\x08\x00\x00\x00\x00\x00\x00\x00\xed=ko\xe3F\x92\
+\xdf\xe7W\xf0\x9c/1N\xa4\xfa\xfd\xf0xf\x81\xdd\
+A\x82\x00\x17\x1c\xb0\x9b`\xf7[@K\x94\xad\x8b,\
+\x1a\x92<\x96\xe7\xd7_U\xf3\xd5$[\x14\xe5\x91'\
+A`;\x89\xa9\xea\xeaWu\xbdY\xad\x5c\xffm\x7f\
+\xbf\x8a>g\x9b\xed2_\x7f\xb8\xa0\x09\xb9\x88\xb2\xf5\
+,\x9f/\xd7\xb7\x1f.~\xfd\xe5\x87\xd8\x5cD\xdb]\
+\xba\x9e\xa7\xab|\x9d}\xb8X\xe7\x17\x7f\xfb\xf8\xee\xfa\
+\xbf\xe28\xfa\xc7&Kw\xd9<zZ\xee\xee\xa2\x9f\
+\xd6\xbfog\xe9C\x16}\x7f\xb7\xdb=\x5cM\xa7O\
+OO\xc9\xb2\x04&\xf9\xe6vz\x19\xc5\xf1\xc7w\xef\
+\xae\xb7\x9fo\xdfEQ\x04\xf3\xae\xb7W\xf3\xd9\x87\x8b\
+\xb2\xc3\xc3\xe3f\xe5\x10\xe7\xb3i\xb6\xca\xee\xb3\xf5n\
+;\xa5\x09\x9d^4\xe8\xb3\x06}\x86\xb3/?g\xb3\
+\xfc\xfe>_o]\xcf\xf5\xf6;\x0fy3_\xd4\xd8\
+\xb8\x9a'\xee\x90\xa8\xb5vJ\xd8\x94\xb1\x180\xe2\xed\
+\xf3z\x97\xee\xe3vWXc\xa8+#\x84L\xa1\xad\
+\xc1\x1c\x87u\xb5_\x01)\x0e.\xc6\xb5\xfa\xb3\x03\xf9\
+\x1f\xe0\xdf\xbaC\x05H\xb6\xf9\xe3f\x96-\xa0g\x96\
+\xac\xb3\xdd\xf4\xd3/\x9f\xea\xc6\x98$\xf3\xdd\xdc\x1b\xa6\
+\xa2~k\xde\xd6\x91\xac\xd3\xfbl\xfb\x90\xce\xb2\xed\xb4\
+\x82\xbb\xfeO\xcb\xf9\xee\x0e\xb8\x81\x19\xf7\xf1.[\xde\
+\xde\xed\x9a\xcf\xcb\xf9\x87\x0b\xd8\x1f\x17\xa4\xf8\x5c\xad\xe0\
+\xaa\xe6#\x92pV\xa0\x96\xc3\xfaM\xc2$4\xdaX\
+\xad\x88Ci1_k\xb8y>\xc3\x15~\xb8xH\
+o\xb3\xf8K\x9e\xdf'0\xed\x97\xf6\xc0\xf9\xe3\xee\xe1\
+q\xf7[\xb6\xdfe\xebb\x1c\xd8\x99\xb7M\xd7\xec\xfa\
+%\xad=\xd6\x03d\xfb\x87|\xb3\x8b\x17\xcbUV\xcc\
+6\xbd\xcb\xef\xb3\xe9\xc3r\x0d\xfb\xde\xe4\xf00\xdbN\
+\xf3\xfd\xf3m\xb6\x8e\x973`\xb5)\xf4[\xa57\xab\
+l\x9a\xcevK\x07\xb8OW\xab\xa90{a\xa6\xdb\
+u\xfa\x10\xc3\x80y\xf2\xb0\xbe\x0d\xce\xb4\x9f?\xc0\xd1\
+R.\x83\xad\xcfu\xebGh\xbe\x9eg\x8b-\xa2\x15\
+T\xc7O\x5cP\xe2\xda\xa0\x15\x18'K7?n\xd2\
+\xf9\x12\xc4\xa5\xc0\xf3\x86\x9c\xe5\xabU6\x83\x93KW\
+O\xe9\xf3\xf6\xa2F\x80\xa1\xda]\xb9\xa5\xaa\x1c\x14\x86\
+\xdd\xee\xf2\x87\x0a\x17\xcec\xf7\xbc\x02\xb2 0\x86\x11\
+\xf3\xcd\xd5w\xf4\x86I\xce\xdf;P\x0e\x0c\xb4\xdc=\
+_\xd1\xf7\x17M\x9f|\xb1\xd8f01\xf1`\x8em\
+\xa0\x07\xcc\x05Je\xfau\xb3\x91\xd0l48\x1b#\
+\xf5l\xd7\xd3\xf6\xb6\xbf\x8e\x8cNn\xaf\xee6\x19\xe8\
+\x99\xef\xfe\xf3\xf3\xff\xfc\xf4\xe97\xfb[\xac\x06\xc8\xcc\
+\x0c5\xa2n\xbf-\xa1\xbf\xae\x97;\xd0$\x8f\xdbl\
+\xf3/\x94\xc6\xff]\xff\xba\xcdzX\xbfl\xd2\xf5\x16\
+D\xff\xfe\xc3\xc5\x0e\x1fW\xa0|\xbfg6\xb1\x8a+\
+i'1\xb0L\xa2\x14\x93\x97\xcd\xfa(\x90DX\x90\
+E\xab\x1a\xd2<\x03\xd4\xc8\x84s!H\xb3\x96=\x03\
+\x5c)p\x04\xe3\xe12\x1f\xb7\xa6\xe2\x01z\xc1v\x03\
+T\x18\xb7K\x5c\xab\x15\x09\x17\xdc\x10\xdeZ+%\x0c\
+\xc0B\xb7\x96jT\x22\xb9\x94\xd6\xb6\x96\x0a\x03H$\
+\x869\xc4\xca!\xb6\x0c0\x9c\x9c\xe1\xef\x00{W\xcc\
+\xa5X,\x0f\xb3r\x85\xa5\x85f\xf1\x91)\xb3\xc5\x82\
+d\x8b1\x12\x95p\xca\xadPz\xdc\xc4$\xa6\xc3\x13\
+\xa7iz\x93\xdaQ\x13k\x06ZB){x\xe2\x90\
+ \x86\xe8\x0bG'\xe9\x18\xfa\x0a\x8f\xbe\x7f\x06\xe1=\
+\x95\xad\x03\xc2{\x9f\xee6\xcb\xfd\xf74\xb1\xf8c\xe8\
+\x84\xc0\xaf*>Y\x09r\xccXB8cz\x12+\
+NA -\xb3\xdf@\xa6q\xa1\xe9\xeaU\xc8\xd8\x1e\
+\x1a\xc9h\xceFF\xa0\x95\xfb1\x8e\x8c\xe5'\x0aT\
+\x14\x0a\xf6\xcd)>R\x0d\xbb\xe5\xba!\xe2l\x8fD\
+D\x04\xe6i\x90\xd93\xba8\x04\x87\xa0\x0dt\x11\xc4\
+]\x04q7\x1f.\x90\xb0\xe0\xfb\x1cS\x95'\x91\xb5\
+=\x86\x16\x96\x0ds\xa9%_O\xde\x03&\xc6 \xe3\
+Q\xde\xe5G\x9dP\xaa\xba\xdc\xa8\x8d\xe6\xa6\xcb\x8d&\
+\xd1\xd2\x18\xde\xe2F\x0aS0nGX\x98\xd3\x1d\x1b\
+G\xad\xf1\xae\xc6\xc2\xfd\xbc\xd0\xb1\x81\xb9\xc4I\x8eM\
+h\xb6\xd1\x8e\x0d\xcc\xa6\x8e\xe9\xc6s\x08u\x87\x9e\x94\
+\x9aa\xe1\xf6\xb8s\x1c\xf7\xa14\x1a\x9d\xc8\x96 \x82\
+3c\xb4$\x9e6CA461\x82\x1a.[\x82\
+\x08\xeaPjmmK\x10)\xa0j\xc6H\x7f5}\
+%B@\xfbRI\xb8\x14\x13\x88\xa2('\xe0n\x10\
+>)\x1fQ\xb7 \x82\x90\x0a\xb4\x09O,W\x94q\
+>\xa1\x14,\x22\x01\xddz9\xc63\x0a\x90q<\xa3\
+\xcc\xf9b6\x8aQ\x82lI=\x0f\xf8\xb0\xafPE\
+w\x9d\x01\x13)A\x8b\x12O\x90\x83+\xbcI\x17r\
+\xd1q\x99H\xc2\x80\x8e \xee\xec\xfd\xa8\xf9Up~\
+-\xa5b\xd4;\xc7\xe0\xfc\x86\xdf\xc8\xec\xa6;\xbf\xb0\
+\xe0MZ64\x7f\xd0=\xd1\x8b\x99\xea\x8e\xa5\x8d\x10\
+\x86i3Z>\xc1\x90\xff\x01\xf2\x09+\x1c\x90O\xcd\
+\xbc\x83tfP\x83\xee\x06\xcbF[\xd2'X\xa2\xa4\
+\x96\xac%|}\xd4E\x08\x15d\x8f\xb3DR\xa6U\
+?\xd4\x09\xf9A\x1a\xf8\x8b\x17\xe2f9\x08\x86\xd0N\
+\x0a-D\x02 r\x13\x87`\x15\xe3\xe8\x1aQ0\x14\
+\xda\xc0\xa3\x92\xc0\x98`\xd9/G*\x9bW\xb0,H\
+\xe9\xf1LU\xb8)/\xb5,\xcc\x0c\x88\xf0\xc8\xd9\xc6\
+[\x16f\x8er\xeeb\xb9\xdae\x9b\x9agp\xd6x\
+\xb9\x06\xd0C\x0e^\xc32_\xc7\x05\x06\x9c\xc4\xf6\x9f\
+?\xfe\xfd\xe2$B\x17]a\x19^Nb\x91\xfd\x98\
+>n\xb7\xcbt\xfd\xf7\xd5\xe3\xc6[\xf4\xb1\x01\x91>\
+\xf3O\xd9\xe7\xa5[\x16j\x14\xa54\xea_\xdd\xd9z\
+{\x06\x98\xddxD(\x96\xf4jb\x0bq\xaf\x8a\xb9\
+\x17$\x06DW\x04l\xd9\x8b\xa3\x0f\xe7.S\xf4_\
+\x85!\x0a\x9eA\xe4\xc0\x00\x1a\x10.\xd1q\x94-8\
+\x80V\xd3\xb6\xa3lUb\x94\x91T\xb6UD\x0fw\
+\x11\xc4m\xe9\x88\xf3\x8bfM\xcdo\xe4\xf8a\xd6\x22\
+\x96\xf1@`|n\xe7\x0f\x837\x98\xf1hf\xeb[\
+\x88\xa9\xa0\xaf!\xa6\x10\xca\x19\xcb\x055\xc3R*\xe8\
+A)=_b\x80\x92\xdfb3\xc0o\xe8\xf5\x9e\x9a\
+\xd6s\x81\x13X1%\x8c'\x18\x18:q\x9epb\
+\x98\xe7\xe4\xba\xd0\xc9\x9a\x84\x1aF<\x5c\xe6\x1cb%\
+\x8d\x1a\x9f\x9ako\xe5\x84\xdc\x5c{\x8d\xd4K\xd2U\
+\xf98\xa3\xa4\xa7\xa1\xaa|\x5c\x01\xfd\xba|\xdc\x0f\xee\
+'\x94\x1f\xb2\xb1\xfa\xfal\x94\x04\xff\x85\x07F\xd7\xf4\
+x\xf6\xe9\x8f1\x05\xec\xe4 \xeb<\xa6\xe0u\x9d\xc5\
+\xd7L\x9a\x10A\xbdLhX\x84OM\xeem\x1f6\
+Y:\xff9\xdb\xdd\xe5xD\xd9\x02W\xd5\x16p\xc3\
+\x13E\x88l\xe7F\x804\x84C\x10\xd9\x16p\x10\xed\
+\x84\xa3\xe7\xe5K\x10x\xc3\xe8\xae\xbc \xc0,6\xfc\
+\xad2\x1fD\xf0x \xf7\xe1\xe1\xd9X\x86\x06Mh\
+\x91\x00U\xc3\xb2z\xc0h~\xbb\xa4\x0b\x84$@\xd6\
+?\x81\xd5eZ\xbc\x8asL-\xa3\x82(\xd6\xd9|\
+\xd79\xd6\xea\xcf\xe5\x1cs{\xbe\x9c\xf2\x9f\xd29\xfe\
+&\x9e\x1c{%\x9e\x92BA<e\x87yJ\xb0\x83\
+<u~\x83\x10\xe4\xa96\x8a\x12\xe2\x0c\xefj\xeb4\
+\x07a`\x098\xe6\x12\xc1 \x80y4\xca\xb1\x15\x91\
+\x9c0m\x90\xdd$\xd1R\xc9I,T\xc2\x158\xb6\
+\x98qg\x98p\x07\x93\xd9\xc9\xb8\x13\x9aHNE\xcb\
+\xacP\x93(0\x22\x9a\xb6\xccJ\x1f\x15\x80\x12P)\
+\x0c\xcb\xce\xfb\xfe\xa7c\x828e\x83\x22\xeb\xf9\xf6(\
+K\x1a_\x89\x81\x19n\xcb\x92@\x1a1FhK\x96\
+\xfa\xb8\x8b n\xf3Ff\xdcA\xbeB\x9a\x08\xa9\xf0\
+\xad\xcc0\x9e\xf3\xb7\xb3\x85\x9c\xfe\x11/ \x8eq\x15\
+{\xc9\xfb\x87\xd7\xe5\xbd\x93\xbd7n\xac\x8eu\xcc\x0f\
+1N\x9d\x060\xd6\xc6\xa2\xf5\x8a\x7fdH\xb5\x98\xe1\
+o\x87\xcfN\xe3\x1d\x83\xbf\xdd\x11B\xae\x9dd\x0c\x02\
+,\xdd\x8d\xe4\x8b\xea\x1c\x02AV\xac\x8f\xbb\x8e\x80\xc9\
+cv\xc0y\xd4\x8cs\xad\xa5=\xb2g\x92-\x025\
+\x16c\xa6\x96.\xbd\x13\x9a\xdaX\xc2\x84\x1f\xe7\x84\xcb\
+;xF\xb3~\xc5\xc3\x98\xa95\x1cp\xe8\xcd\xc8\x91\
+\x19g\xd9\xecfvs\xe8\x80O\xab\xa5\xe8GK\x1c\
+\xb8\xff\xc8\xd2\x99 /\xe0Kb\x95y\xc9\x19\xc1l\
+\xfct\x22\xa5\xd9l\xd1\x93\x82\xf7\xe7\xa2\x92\x90\xd4\x80\
+\x8f\xa1\x8fQJHFb\x1af\xed#l\x95ep\
+\xce_%\xc5\x02\x7f\xc7H\xb1(\xde)\x84,\x81\xb4\
+\x0a\xd5\x10?~L\xb0U\x16\xdb8\xf4\xa6\xf1\xc8Q\
+-\xd4B.\xe4y\xf8\xf9k\xfcFK\x87j\x84\xf0\
+ec\xcbSS\x1c\x8d\x85\x9f\xbaw)>\xacH\x91\
+\xca\x83\xa2\xab\x16\xc0eE\xceD\x8d\xae\x98y\xc5\x1c\
+\xca\xd1\xad{\x19\xcb\xf1\x89\xc5\xd7#\xd0\xa1\xc0\x0e_\
+\x0b\xbb\xd2+\x81Q\x9e\xc1\xfc\x8a!\xfa\xf8K\xfcW\
+%\x9d=\x91tC\xc5\x0c\x8c\x13\xad\x80\x11\xbb\xbb\x8d\
+\xa5L\xa8\xa1\xd0:aHI\xd8\xb6\xb9|\xa5\xc3\xf8\
+#\x89)\xf8k\x11\x93\x96\xd5f-Zb\xda\x0e(\
+a\xe8_\x92\x96\xfa\x9b\xd22.\x13\x9e\xe4/IL\
+I_]\xcay\x8b\x9aVb])\xb5\xafK\xcc\xeb\
+)\xdeKpO\xf5\xdd\x0d\xbcJ1\xff\xbc\xcc\x9e\xde\
+\xd5\xd4\xb8I\xeb\x9d\xe1\x85\x0eg\xd7\x81\x86E\x0cZ\
+6\xdc\xe4\x9by\xb6\xa9\x9a\x94\xfbi5\x95\xa6\xbf\xb9\
+-\xe2\x1d\x19\x8eZ\xb7\x93p\xfb\xf6.\x9d\xe7O\x1f\
+.X\xb7\x11\xaf\x97`\xc0\xa6\xa42R\xf4\x9a]\x90\
+\xc8\x13\xad\x89\xe5\xa6\xd7\x88\xeb!\x22\xa1\xa4\xb1Cu\
+\xe3<\x9f=\xe2e\xa6\xf8\xb18\xea\x87}\xaf\xfb\xe3\
+f\x83\x08\xab\xf49\xdb\xb4\xaf\xd64\x97o\x8c\xa9;\
+\x96\xd7s<\xc8\xf6.\x7f*\xe8\x83\x95\x9b\x8fYw\
+\x06l\xf7w\xef\xe1`\xd3\xed\x06\xcf'\xd4\xf1i\xb9\
+\x86\x0equ!\x88\xca\x1eaJ\x8cj\x99\xba\xae/\
+\xeab\x00\x01\x03t-\x1b\x9f\xf1\x15\xa2\xbf\xa2\xc7\xe5\
+<\xdb\x86\xd7\xe4\xda\xe2\x9b\x9b|\x1fn\xcfo\xfe\x0f\
+\x047~Hww0\xc2\x22]m\x0f\xa1\xacs7\
+\x89\x8fR\xb4\xec\xf2U\x06\x227\xcb\x1a\xb7\x14i\xe4\
+\x83M\x8b-\xcb\xa3\x0b\xd2\x1e/\x04\xdd\xae\xf2\x9bt\
+5H\xe3\xfbt\xbf\xbc_~\xc9\xe6\x8d\xd7\xdf\x1e\xc3\
+\xdbq\xa9\xc2\x1a\x92\xc0\xe2*\xf9\xdc=\xe3\x95\xaf\xfd\
+3\xc2Z\xda\x08\x01L\x88\xc6I\xc3\xab_\xcb\xf5\xed\
+\x1e_\xb6\xa3\xa0\xd5\xecT7=\x07\x9a\xb2\xfb\x87\xb2\
+\xb5\x91\xa2(\xfa\xbc\xdc.o\xd0g\xf7v\x08\xb8k\
+\xbc'5\xef@q7%>\xae\x09\xb5\xe46_\xaf\
+\x9eK\xb4J\xa9\xf4u\x89\x83\xdfg\xbbt\x9e\xee\xd2\
+F\xb1T\x10.h\x951\xb9\xde\xcc\x17W\xff\xfc\xf4\
+C\x1d\x8e\xccfW\xff\xce7\xbf7!\x06\x22\xa47\
+\xf9#\xf0l\x1d\x9f\xe1}\xab\xd9\x15\xea\xd9t\xf7q\
+y\x0f\x02\x83\xf7\xf8\xfe{\x7f\xbf\x02\x15W7\xb4\x90\
+\x91\xda\xcd\xa0\xc5\xb0\x9b\xac\xb8\xa7\x17\xbc\xda8\x9f\xdd\
+/\xb1\xd3\xf4_\xbb\xe5j\xf5\x13N\xe2\x85M\xe5\xa0\
+\xcb\xdd*\xfb\xe8\xe6,\x1e\xab]L\xcbmTA\x8f\
+\xb7\xcb\xebiE\x06\xf7\xe9\xb6!\xcf\xadVu\x22{\
+\x17\xaa\xf06\xa0\xd6\xa4\x92LMb\xbcz\x90\xc5\xea\
+\xb2\x22\xe3m\xcdU\x01\x0b\xc4\x85`\x8a\xa1\x01\xa7\x09\
+3`\xb3\xb1X\x8fY\xc9a\xc2\x89k\x96V\x13\xbc\
+\xd2@t\x02\xce&\x07\xbfS\xf2D\x12a\xd9e\x9b\
+5\xb1\xd4\xb7\x09\x937 \x82\xbd`\xb0\xa9\xc4\xb4\x9a\
+\x12\x0a\x91\xe4\xfb\x05P\xf0\x0ah\xfb}\xc7\x10\xbbk\
+V\x97\xae\xd9\x0b\xeb\xb7\xbbM\xfe{v\xb5\xce\xd7\x0d\
++\x96)D\x98O\x0b\xe5\x07\xdb\xa5\xc6\xeb$\x8f\x1b\
+u\x8c\xf5\x0e\xae,\xd1ks\xaf\xbd-P\x80\xb7J\
+Y\x81\xafc)\x12\x10=\xdd\x1a*DRM`o\
+\x10\x8fL\xbc\xc78\x0c\x06\xa3\x7f\xe9\x07\xdcm\xa2\xe1\
+\xa4B\x83\xec\xd2Vi\x83[\xa2J\x14k\xec\xaa\xbf\
++\xeen\xa5\xb0V*\xa5\xd2\xfdX7\xa7x\xef%\
+NA;\x1d(\x0f\x1e:\x1d5\xf2t\xceL#\xb4\
+\x09\xde:+\x05S\xa8\xcct3\x1b\xdaD\xaf\xd0\xdd\
+\x98\x13X\x0cg\xd6\xc2\xb4\xf29\xd5\xf4\xeeE\xa2D\
+\xd2\xb7\xd8\xa9iw\x97M\x9c\x226\xa1\xf6\xcd>\xc0\
+\xa8M\xebs\xa0\x15\xb5fD\xa5\xc6\xd0EL\xaa\xc1\
+\xa3Y\x84wh\xac\x90BF\xa0\x0b\xb8\x01\xc50)\
+:Gq\xf1\xb7\xfeX\xe0\x81\xa0\xd7-U\x8f\x06P\
+\xe2\xc21\xd4\xff\xae\xa2\xb2\x91D_B\xcb\xdd\xee\xd2\
+M7\xf1U\xb5ekX7O\xa8\xa0\xd22}>\
+6\xa1\xd4\x82\xefF,x\xcc \x1c\x9aI+\xed\x80\
+l\xbdH!Yr\xb2B\xb2\x01\x85\xe4\x0eJ\x9a\x83\
+\xb2\xeb\xbf\x96\x18\xd4H\x01\xe5pNa\xbb\x9e\xde\x96\
+\x0f\xbe\xcc\xf5g\xa0\x89\xd4\x960\xa5'\x18\xd1X\xc6\
+\x85b8A\xfd\xdc \xc4\xb0\x13\x89\xaf-\xc1\xe0p\
+\x99H\xce\x98\x97\xbe(8\x1a\x0c\x0d\xd7\x10\x13\xd5\xd7\
+G\x1cKS<\x22\x09\xa1e\x14\x9b\xc4\x80M2`\
+\xa9\xaa\xfb!Q\x5c>\xb6@U\x0fd\xef\x1a>\xa9\
+\xba\x9b\x16\xb0\xe9G&M\xcf\xa8\xc2\xf5\x11\x82\xbd\xfc\
+\xc9\xaa\xb5\xd4\x13\xf5\x17\xd7\x88MK\xc6\xfb\x17^Z\
+\x0ab\xa09|\xdf\xa6\xa5\x9eZ\xf7sj}\xe6_\
+\xfa:\xae/\xc1\xe8\x8f\x91\x80\x80V\xae\x99j\xc0\x1d\
+\x81xPIK\x84\xbbG\xa0(\xb8\x18\xcaqj\xfd\
+\xdc \xc4\x94\xe3=\x02\xc5\xc05Q*1\x10*\x8b\
+\x9eCB\x14V*\x8d\xb3\x1cM\x5c\xe1r\xd9\xbe0\
+W-H\x86G\x885b|\x1d&\xc0\x15b\x22\x84\
+\x96o\x96\xb7\xcb5F\x0a?G\x10\xc4\xe3}h\xd0\
+\xa7\xael\x0e\x96\x1b\xfd#\xa2\x10\x90+\x108\xd3\x00\
+\xdduP\x87'Ub\x0d\x07y\xf1`Z4}k\
+\xa0\xc5\xb2\x02c\x19\xf5\xc6\x03!\xae\x06\xac'n`\
+\xd0\xdb\xda\x04\x5c<\xc1<(\xa3<\xe1\x94\xf9\x03\xd6\
+ o\xe2\x1a\xe6-\xb0\x1e\xad\xd9G\x7f\xbf_\xa2\x16\
+\x19d\xf9\x80\x8b\x01\x86\x84\xe7\x06\xc6\xd0Aq\x9d\xa4\
+I\x84T\x1c$\xa5\x81\xb5\x16S\x01-I\x0c*\x14\
+\xd3\x0c\x07Q|k\xfb\xc2\x91\x5c\x82\xc9\xe1\xa6h\x94\
+\xcc\xef\x07\x9f|\x02\xc3\xc7f\xf6\xba_\xbd\xc6\xc0V\
+\xbeD\xe3}\x0e\xbc\xbc3$B\xef\x8b\x9a\x99\xa2k\
+s\x87\xe22\xe4\x8a`\x01W\xd7\x1d\xa8V7\x11`\
+cA\x1b\x80\xee\x8c\xf1\xee\xac\xb1\x85\x99\x07\xa9\x11\x0a\
+\xe0\x13*\x12\xa2-'\xca\x83U\x0f\xa8n\xcb>@\
+\xad\x02\x916\xad}|o\x06\x84\xe1\xfc\xb1\xebg\x14\
+\xa8\xbf\x1a\xe4\x8d\xef\x16e\x09Q\xa8\xa8\x01\xd3\x15i\
+5\x08q\xbf\xcf\x97\xe8\x1e\xd7\x95\xe01\xcd\xe0`\x12\
+\xbc\xc9\x05\x9a\x22bx\xa1\x10q\x91C\x15%\xc8\xd3\
+\x15\xa8z\xc0\x9eE\x0fh\x8d\x1d\x22\xe1E3\xce\xd6\
+\xc2G\x80C\xe6\x8eb\x05h\xe2:Y\xaa\x08m@\
+%6\xee\xc6\xe1s\x0bB\x81h\xee\xe6w=\xb7\x8f\
+^0\xcc\x11\x83\x0d\xd3P\x8dQW\x91\x16\xac?R\
+\x89%\x96\x92\xd1\x89J\x984\xc4\xdd*?\xe8\x1a\x87\
+F6TS\xa6\xa9-G\xae?r\xe0\x16\xd8\x1a\x9c\
+\x01\xac\x1f\xc2Hk\xf5\xe5\x00oA\x88iy\xcd\x5c\
+\x16\xdc\x1c\xed\x98\x8b%(E\xc8[\x12v\xccE\x03\
+*\xff\x16\x9c\x85\x1d\x94\x8a*\xb4\xba\xb1\x8b\xec\x8d\x8d\
+ T?\x8e\xaf\xa4\x01K\xc4=X3z\xb9\x1e0\
+\x07\x8e\xaf\xf0\xde\xa1i\xda\xe3~\x97\x82\xad8\x90\xa2\
+\xe0+\xb0\xa1\xd6P\xc7Wx\xcf\x19\xfcjP\x14\x10\
+\xec\x98\xf2\xb3\xfb/n\xa3DD\xfeE\x14p8]\
+\x9b\x1b'np\x8b\xcf\x0e[\x0b\xea\x18\xaa\x84\xba\xca\
+0c\xb8\x01\x9d\xe5\x01\xebN\xb8\x99\xb2\x1b\x8f\x0aT\
+\xca\x8av\x19\xb5\xf1\xdb,\xd5\xc4*$\xf0\xee\xb9q\
+y\xa5 \xa0\xc9\x15}\x7fPY\x092h\xefC\xca\
+J\xd0\xb6\xb2z\xb3\x8aoV1\xe0C\xe1\x9dd0\
+*\x96\x04\xa3\xe1\xb0KvP\xd3\x1dSO\x9a\xf5\xb4\
+\x93\xe4\x07u\x93fQ\x892\xac\x98\xdc\xa8\x1d\xbd$\
+\xad\x12\x03zII\xaeJ\xbd$\x88<Y/\x81\x8a\
+i\xa9%\x94\xdf\x90Z\x02\x17\xb9RK`\xf2\x8e\xa9\
+%\xd0n\x01\xb5\xa4\x88f\x83jIRm\x0a\xb5$\
+\xb9<I-\xb1V\xec{<\xd3dG\xe7\x01\xdf\x94\
+\xcd\x9b\xb2\x09+\x1b\xc5\x05\x16l\x9fA\xd9\x04\xc3V\
+N\xc1?\xe3\xd5;\xf1\xf2S,\x13L\x00Ye]\
+ZMi\xa2\xb9\x1cr\xab\xe0xk\xad\xc5 \xa4\xa5\
+\xa5\xc7\xce\x9cHQ\xf7\x1a\x97\x09\xee\xc1\xaa\x87Bw\
+a\x1fp|*\xc4\xa6\xb5\x8f\xef\xcd\x80\xb0\xc2\xbf\x05\
+\xaf\x8f\xa3_U\x00\xbc\xb1\xcb\x05\x19\xa9\x9d\xf3\x0c\xce\
+\x9b\xb5\x1eB\xdc\xedQ\xf9\xea\x95\xab.\x0dj.\x8e\
+70\xca\x9d\x08\xc1\x98T\x0d\xa8z(\x1cup\xf0\
+\xac\x15n.\x01\xde\x0fw\xcd\x85\xa2\xf4;8H1\
+\xbc-T|\x01t\x9b\x11\xa0g@B\x1aX\xd5\xa3\
+p\xd7]\x1f\x1a9D^\xb6\xba\x05\xb4\xf0\x1d$\xac\
+\xc4\xec\xb0oE-t5\xe2\xb0o\xc5F\xe6R\xde\
+\xd4\xda\x9bZ\x0b\xab5\xcc\xe12b[\xdf\x07rN\
+'\xca\xaa&\x81\x00Q\x18\xd1\xa5:\xaa\xf2\x07\xe0\xbd\
+\x09\xa6\x1aP\xf9\xb7\xd0E\xae\x83\xcb\x1eP\x97\xb0\xab\
+\x1a\xbb\xc8\xde\xd8\x08\x92\xaa\xf4\xa5\xa0\x17t\xf3`\xcd\
+\xe8\xe5z\x84,b<*-5M{\xdc\xefR\xa8\
+#V\x88\xbfSH\xb0C\xa6\x8b\xdc\x81*\x14\x12\x83\
+\x11!`-\x01\xc5\x9fB\x19!\xaeRN\x19\x01\x12\
+\xa7\xae\xb1p\xdb<lY)#@W\xb6L\x1cT\
+\xdb\x81\x9e\x86H\x1c\xa4\x01\xd6\x9d\x0a}\x84\xdd\x8c\x88\
+\x0aTk&\xe5\xd8Q\xbb\xc3Am$O{yg\
+\x07_\xde\x05\x93PZ\xbc\xc5uo:i\x84N\xe2\
+L\x18\xabZE\xe6/\xd5I_\x91\xa3\x077\x0c\xe4\
+\xd9\xb4^\x91\xbf1\xed\x1b\xd3\x86\xd4c\xff\x1e\xf2\xc9\
+Y/&\x82)zn\x87S\xf4\xda\xcb\xd1\x13\x0b\x82\
+Sz\xb1\xb6\xccg\x1b!\x09\xd5\x0d\xa8\xfc[\x98X\
+\xd7\x81G%\x1a\xab\x1b\xbb\xc8\xde\xd8\x08\xaa=d\x97\
+\x8c\xa6\x1e\xac\x19\xbdX\x0f\x87\xf0\xc5\xd9=C\xb9\xd6\
+M{\xdc\xefR\x98XR\xe4.\x9c\x89\xd5\xda\x15\xb6\
+\xe2\xf0\xe5f\xa4\x95\xda\xc8\x0aP\xfc)L,\xe2\xd2\
+\x221/\xf15\x02\xc7\xab*\x95\xbf_\x22\x8b\xda\xdd\
+\xd7\xeeB\xb7k\x13\xf5f\xe0\xaf\x92\xc2\x07\xd6}\x0a\
+\x03\x0b\xbd\xa4U\x91C\xc5o\x06,\x87\x8eZ\x1dF\
+e\xe7%\x97\xcc\xa8\xaah\xb7\xfe\x08\x1c\xc9\x94\xb4\x1c\
+\x0e\x22\x11\xca\x0a\xad\xcd\xb1w\xe9My\x5c>\xcfP\
+\xc7\x81\xe6\x9am\xb7\xdbf\x09\xcd{Z\xdd\xbc\xbcu\
+|$q\xdd\xd2\xc2\xb9P\x86R\x05\x8bp\x8c\xc4A\
+\xf1I|w\x817\x93\xf1\x8b\x94\xc1\xe9\xd0x1Y\
+BDH\xf1\xd5\xa5\xa4R\x15@+1N\xa5\xa8\xaf\
+\x8cf\x10Xb\xc8\xc3\xb8\x9d\xa8DP-\x84F\xff\
+M\x80<\xc0,\x16B1x\x8c\x98\x04v\xc0\xea\x0d\
+\x80 sB\x9c\x86\xdf.\x0aS\xe1\xfb\x7f\x98\x07\x5c\
+Bf\xe0\x1c\x89A*\xd3\xc40\xae\x15G\x98b\xc4\
+h\x80a%\x19\x84\x7f\x1cdY\xc33\x9c8,\x97\
+\x09\xbcH\x8dN\x9d\x02\xd7S\xe2\xeb}\xd0O\x04\x1c\
+8\x15\xdc\xab\xf7\x0a}\xd8\xdd\xc1;\xe0\xa3\x22-\xaf\
+n|\xbd\xcef\xbb|\x13\xcf\x1e7\x9f\xd3\xdd\xe3&\
+\xc3j\x96\xfa,\x8f\x96C\x94\x89\x00\xe9\x8a\x1c\xac\xb5\
+\x04\xc3eW\xa0\xa35a\x9d\x82\x07\x8dE\xdb\x94\x03\
+J}\xff\xd4+\xe2\xf1\xaax\x8e\x95\xf1\xc8@\x1dO\
+\xa8\x90\xa7\xc6\x8f\xea\x81\xab\xc6\x0e\xae7p\xd9R\x0f\
+\xdb^\xc2\x81z\x86N\xcd\xd2P\xb5S\xab\x92\xa1\x7f\
+\x11\xb7U\xc9\xd0\xbf\xbd\xdb(\x5cJ\xba|\xe1\x05\xe5\
+L\x00[\xd2\x81\x17\x1e\x9c\x8e+\x08\x1b*p\x08\x0a\
+x\xbf<\xed%\xabc\xe3R\x06\x0d5\x18\x19K\xc1\
+\xb1\xf4\x1f:\xba\xc0\x91\xff\x059<X\x7f\x8b_\xf8\
+ \xc1\x0c\x1b\x8b)xP\xffL\xd5\xd7\xd1J\xdd\xdf\
+\xaa\xe7\xe5\xb6\xfe\xaa\xd6\x1e#@$JE\xf3\xc5\x89\
+\xad\xca5<\x1c\x5c\x91\x17\xef\xed\xcb\xbb\x1c\xde7\x14\
+U\xf5dE\xa5\x99\xf5\xbe9\xbc,A\xe3xC\x07\
+\x1c\xc4\xce\xf7\x8c\xc2D\x9c\x8a^AP\xb34C\xdd\
+/'\xd5O\xc1\xac\xdf-\x88\xfb\x12\x82\x01\xdel\xbe\
+\xa4\xc2\xdb\x8dGJ\xfc\x1fr\x00\x19\xe9\xc4\xbb5\xd4\
+\xa7\x8cbF\x02qN\x9f\xbe\xb7M\xeb\x15\xef\xb4\xea\
+\xf2\xacG\x94\xfa\xff\x9aR\x14\xcez-{,\xbf\xeb\
+P\xfd\xd9\xbf\x80\x13\xdc\xefs\xf0\x8a\x0e\x0c\xa5\xf1k\
+e\x8c\x7f3\xf7\xf0\xd4\x07W\xeb\xedm\xe0\x08K\x1a\
+\xb2\x8a\x84\xf4d\x1a\x1e8\xb5!f\xafk\x14{\xcb\
+\xd1\xc6\x9d(?\xc0\xecAr\x1dbw\x8d\xb6\x16\xa8\
+\xc8Nb\xf7\x01\x83q\x88\xdd\xf9Bff\xde\xa6\x15\
+l\xc5\x82\xc2U\xaa\xfc\x16,\xf89.\x00cg\x0c\
+\xb85Xe2\xda\x1a\x94[\xed\xb1|\x9f0\x15-\
+\x9d\xda\xa3\xbe\xea\x08\x10\xfe\xe5\x1c\x7fN\x8de9?\
+\xf9\x08\x83\x04\xe5r\x90\xa0\xc7TX\x15!LP\x1e\
+\x8a\xe8`@\x97\x9d\xb40{\xdaICH\xd6\xa5\x9f\
+\x93\x0d\x0d\xff\x8c \xb9Sn\x1d+\xf3\xdc\x9c\xd9\xa1\
+\x83\xee\x9eih\x98\x16{\xf5\xe4\xb4\xbf\xc8fK\x82\
+\x9e\x87\x96\x82\x8f\xa2\xe5\xb1s=\x85\x19\xe2\xf3r\x83\
+\xd0'r\x83w_\xec(7t\x0e\xe6\x00+\xc4\x95\
+Kg\x0er\x83\x872\x86!\x0e\x0a\xfe O\xd8\xf3\
+PT\xd2\xaf\xe1\x89\xb8\xc3\x14\xce\x04^\xe3\xb5\xb3\x8f\
+\xef\xfe\x1f2\x91g\xd1\x9do\x00\x00\
+\x00\x00\x10\xb4\
+\x1f\
+\x8b\x08\x08\xd3\x9cBb\x00\x03Gnome-f\
+s-directory.svg\x00\
+\xd5Z\xedn\x1bG\x96\xfd\xef\xa7h\xd0X$A\xc8\
+f}\x7fp$\x0f\x12\x09\xde\x0d`\xef\x06\x1b\x07\xb3\
+\x98?\x03\x9al\xda\x84)R\xa0([\xca\xab\xed\x8f\
+}\xa4}\x85=\xe7\x16I\x91R\xb7V\x1e\xc7\xd9Y\
+F1\xbb\xba\xabnU\x9d\xba\x1f\xe7\xde\xe6\xc9\x9fo\
+.\x16\xd5\xc7f}5_-O{\xbaV\xbd\xaaY\
+NV\xd3\xf9\xf2\xddi\xef\xd77/\x07\xa9W]m\
+\xc6\xcb\xe9x\xb1Z6\xa7\xbd\xe5\xaa\xf7\xe7\x17\xcfN\
+\xae>\xbe\xab0ry5\xba\x19_\xbe~}\xda{\
+\xbf\xd9\x5c\x8e\x86\xc3\xe5U=\x9e\xae\xde6\xf5du\
+1\xc4\xa3!$\x0e/.\x86\xbd\xbb\xde\x8f\xf7\xddu\
+\xbc\x9c\xce\xda;\xe2\x01:\xda;\x89\xa7=y8\xc2\
+\xf5E\xb3\x19\xef\x1fL'{\x01\x97\xd7\xebE\xbdZ\
+\xbf\x1bN'\xc3f\xd1\x5c4\xcb\xcd\x15\x84\xe8}\xdf\
+\xc9]\xdf\xc9\xba\x19o\xe6\x1f\x1b\xccu\xb1\xc2\xcc\x1c\
+\xb6\xbcz\xbe\xeb\xb9>X\xd7\xa7O\x9f\xeaOVz\
+\xe8\x9c\xf3P\x99\xa11\x03\xf4\x18\x5c\xdd.7\xe3\x9b\
+\xc1\xc18\x00\xd66\xce(\xa5\x86x\xb6\xed\xf6\x84.\
+\xa3\x9b\xc5|\xf9\xa1s\x0d\xf2\xb4W}\x9aO7\xef\
+O{.]nz\xd5\xfbf\xfe\xee\xfdf\xd7\xfa8\
+o>\xfd\xb8\x02l\xaaR\x95K\xf8\xe3\x09\xdf.p\
+\xb8+(\xc2l\xb1\xfa4\xfa8\xbf\x9a\xbf]4\x7f\
+j\x96c|\x0d\xde\x8e'\x1f\xde\xadW\xd7\xcb\xe9h\
+\xd9|\xaa\x0e\x06\xce\xa7\xa7=\xac\xcd\xf3\xfa@\x8bt\
+\xef\xc5\x09\x17\xb2\xdbU\xaf\xda\xdc^b\x82Ms\xb3\
+\x19N\xae\xaez\xd5\xbaY`$\xa7\xbdz\xdf4\x9b\
+\x22i:^\x7f\x18\x5c\xac\xa6\xcd`r}\xb5Y]\
+\x0cd3\xc3/\x94\xf5\xaeY6\xeb\xf1b/Lz\
+\xdeI[\x8c\xa9\xea\xcd\xf2\xa1\xdc\xd6%\xc9\xe8/\x10\
+\xb3\x14\xed\xda\x8byVU'\xd3fvUz\xe1\x22\
+\x04\xd7\xe3]\xdc_\x8f\xa7\xf3\xf1\xe2\x9f\xf9\x05\x8d\xad\
+\xdem/~]\xce7\x98\xf2\xfa\xaaY\xffr9\x9e\
+4\xff\xb6\xfc\xf5\xaa\xe9\xed\x1f\xbfY\x8f\x97W\xb3\xd5\
+\xfa\xe2\xb4w1\xde\xac\xe77\xdf\xaa\xdaF\x97\xfb\x8a\
+\xff\xe1\xd2\xf7\xb5\xabs\xf6\xb9\xaf\xfdw\xbdjv\x0b\
+\xcd\x88\xb5\xca\x1ag8\x83^\xb8\x5cg\xe5,p=\
+\xedYU\xabdC\xaf\x9a\x1c\xf6\x9a\x1c\xf6\xe2\xc2\xff\
+\xe3\xf5\xab\x9f\xce\xff\xa6\xfd\xdf\xb6K\xc7\xe2\x01\xd6e\
+Q\x0f\x5cx\xef\xf7J\xc6\xf6`\xb2Z\xac\xd6\xa3\xe7\
+\xc9\x9f\xfd\xf0\xf2e\xafZ\xcdfW\x0d4T\x15D\
+\xda\x04\x84V\x01J\xd9\xf3\x1f\xf2\x9d\x00\xbd\x13p2\
+<Fo{\x17:\xd0\x8c\xd7{Ho\x0d6\xa2\xea\
+l\x02\xc4\xdf\xa0am\x1d\x95\xd7\xbd\xeaV\x9f\xf6\x0c\
+\xb6h\x0c\xa4\xdf\xe8\x83'\xff\xcb1\x1c\xc0\x11:\xe1\
+\x08\xa6u7\xd6\xe6\xfc48\x82}\x92\x80\x038\x8e\
+w\xde\x0d\x076\xedc2[8B\x9dB\xde\xa2\xe1\
+j\xaf\xa3.h8[\x07\x1d\xcd\xe7\xa0\x11\xbb\xd1\xc8\
+_\x88FT\x1d\xca\xc1\xcf\xdf\x8d\xc6c\x06\x95}\xb4\
+0&\x93r\x7fP\xbe\xb6\xf7`Z\xd1Z\xd3\x0f\xb6\
+\xce:\xd0\xbe\x08\xea\x80\xa6\x93\xa2+\xa8j_\x07o\
+m\x81\xf5\xe0\x11[\xda\xd4^}\x16\xae\xa9\x13\xd7\x98\
+\xda\x8dN>O\xc2\xb5\xfd`\xbc|\xfe!p5\x88\
+5N\xdb\x82\xab\x89\xb57z\x87\xab7\xe8\x99\xb6\xd6\
+;p\xb5\x11\xa3y:\xae\xb9\x13\xd7\xd4\xee\x8b^\xca\
+\xe7)\xb8\xa6\xd8* \xe7\xb3\xb3\xcf\xb1\xde?.>\
+\xd8\x88\x1bA\x97\xf8\xa0Cm\xbd\x0e\x12\x1f\x10\x05\x9c\
+f\xf0\x9f\x1c\xf6\x9a\x1c\xf6\xba\x83\xd4\xa8NH\xb3\xfb\
+\xc2\xf8\x90\xdb\x03\xcc\xe7\xc6\x87\xdf\x13R\xed\xa8\xc3\x88\
+\x19\xa6/_Z\xd0\xd5\x80\xd7\xd6\x088\xa1\xaf\xc5\xf2\
+\xed\x16c\x9dj\xc5\xb8@\x88m\xaauH\x82p\x86\
+\x19\x04\xfa\xe3\xc9A\x9f\xc9A\x9f\x03|u\x17\xbeA\
+\xb5\x07\x9c\xfb\x1a\xd7\x89oPO\x0b8\xaa\xf6\xdd\x22\
+\xda\xcf\xd8\xda\x10:\xb4\xfe)!\xfcQo\x92U\xe4\
+\x19h\x1b\x5c\x7f\xffU\xee:\x040\xeb\xfa\xbe\xf6!\
+\xc4\xad;\xd1\x1a\x01\x9f\xc6Io\x92\xeb\x10L(\xce\
+\x84\x06a\xb2)\xbed\xf7\xe0\xc9\xae\xc4\x98\xces\xd1\
+_H\x04\x82\xee8\x97\x1f\xf3\xf9\xf1\xb9(\xefB\xb7\
+\x94\xf6\xa3\x09\xeeG},\xc5:\x1b\xbb\xa5t\x90<\
+}\xa6\x8e\xa5\x04\x15\xbb\xd5D\xb7{\xd7l\xcf\xeei\
+Z\xb2\x8f\x08\xf9}<\xecW\xd45\xeb\x8a)\xdf\x14\
+\xce\x85\xe3\xd9\xb2Nx\x87h}*\xba\x86\x86\x93\xd3\
+y\xba\xb2\xd9Ne3\xed\xba\xf2\xe4\xb8\x15L\x87\x9a\
+\x84\x1f\xed\x17\xb2N\xe7jg\xdd\x16\x0e\x04\x15\x13\xf7\
+p0\xcf\x08\xb1\xc0\x01\x0e\x1a]\xf8,\xdbs\x9dp\
+\xd8v\xdb{:\x1c\xb6\x1d\xcf/T\xb2\xdb\x92\x86\x84\
+`\xd3\x16\x0e\x0bJ\xb8c\xe1\xf0D)\xee\xd8\x22c\
+l\xd4\x9f\x93\x93\x98\xce\x14-\xd8/d\xe1\xc1u\xb1\
+\xf0\x10\xce\xce\xbe,'\x09\xc8\xbd\xa8\x107%_\xf3\
+\x8a\xd4V\x92\x92\xfd\x13\xc2aj\xa7\xec\xe7\x90<\xd3\
+\x99\x94\x04\xd7\xeeA\x9eL\x9e\x83kg\xdf_H\x9e\
+oKV\x16\x5c\x8e\xfb\x14M;xB\x81\x03\xd8\x04\
+\x13\xb7\x9c\xd7\xe9\xda;\x97?\x07\x8e\xce\x5c\x22t$\
+\xf0g\xfe\xa5z\x9avt$\xf0\xfa<\xba\xf3v\x82\
+\xf6\x10\x8e\x93!+$r\xc5R\xdft\xbc\x19\xcb\x04\
+\xbb\x86\xc7n\xb7\xa3o\xb0\xc1\x0f\x0dF\xbdm\xde\xcd\
+\x97\xa7\xdf\xfc\xf7\x7f\xfe\xd77\xec\xfb\xcd_\xfck\xf5\
+\xfa\xf2\xacy?\xff\x97\xdf\xd6\xcd/\xbf\xfd\xeb\x9b\xc9\
+o\x1f&y\xfa\xcd\xc9p?\xa6H\x18\xdd\x5c\x5cR\
+r%W\x9b\x0fD\xe9\xe7j\xb3Z->\xcc7\x95\
+\xad\xd5\xc0\xe4~5[\x8f/\x9aO\xab\xf5\x87J\xd7\
+\xe1\x0e\xbd\xdd\x92v7\xc8-\xa7\xb3\xd1\xbf\x9f\xbf\xbc\
+\xbb\xb3\xbdw\xde\x5cM\xd6\xf3\xcb\xcd|\xb5\xac\xd8\x1e\
+\xbf]]\x03\x89\xeb\xeb\xf9t\xa4\xa7\xde\x197\x9e\x0d\
+\xdeNfa\xe0&6\x0f\x92J\xb3\x81\x091\x1b\x03\
+\xcd\x9fNL\xefP\x22d^B\xc6\xcf\xeb\xd5\xf4z\
+\xd2\xac_<\xfb\x81\xc5\xd1\xea\xe7\xf3\x97\xd5b\xfev\
+=^\xdfV\xbeV\xeadx\xdc\xeb`\xf8\xf0\xde\x9a\
+~\xff\xf5\x0e\xff_\x88\xbc\x87\xea\xcd\xf8rt\xc6\xda\
+ps\x8e\xff_<3H-\x06\xf8\xd3\xea\x8d2#\
+\xfc\xe9\xf0=/\x00\xed\xfd\xae\x0f\xe4\xbc^M\xe7\xb3\
+\xdb\xbd\x1cG9&\xbfQi\xe4\xf3\xc8\xc5\xbf\x16\x11\
+\x87\xbd\xda\x97\xb2Z\xbf\x816\xee\xce\xf8\xa7\xc5\xe2\xfa\
+j\xb3\xe6\xedJ\x83\xf0\x1d,d\xd7\xf1\xe1J\xb6j\
+z\xb8\x16=\xd0\xe9\x8dN#\xe7F\xda|\xaf\xf4~\
+O\xc7\x9d\xffP\x95y\xb8\xf4\xd7\xafG\xe7\xab\xc95\
+k\xf9?\x9d\xbfx&\xa2&j2V\xd3\x94\x06S\
+;U\x037m\xdc \xa5\xd9\xdb\x81\x8fa\x12\xa6!\
++\xedg\xb2\x95{\x83\xff/\xb72\x9d\x8c\xc8$\xc7\
+p;\xf3\x8b\xf1\xbb\x86\xa5\xfe\xefo.\x16\xf0uw\
+O\xee\x8f\xd8\xcc7\x8b{Z\xb1]\xde\x0f\x8b\xcd\xfd\
+\xfb\xdb'\x8b9\x8b\xd5\xa3R\xa7\xbe\x19\xc0\x93\x8e\xaf\
+\x17\x1b,g\xb6ZL\x9b\xf5@\xd7\xe3y\xd9\xfeb\
+\xfe@\xf4\xb0U\xb6\xac\xf1\xc1Z\x1e\xc7p2\x19\xfd\
+\x85\xde\xf2\x00\xbbnH>\x03\x91\xdb\xcbFd\xae\x9b\
+\xab\xd5\xf5z\xd2\xb4\xbe\xfc\x99N.\xe6\xec9\xfce\
+3_,~\xa2\xf4c\xd7q\x00\xef\xb1K\x19n\xd7\
+}\xe0\xcf\x87\xf7\x1c\xfa\xc9\xf0\xd8\xe9C\xd3v!\xe4\
+~Pj\x96\x88D\x9f\x8eC\xce\xf1\xf0\x93\xcb\xf1\xe6\
+\xbd\x047^x\x16\x9eq\xfd\xbar\x11\x09\x81\xef\x93\
+\x08'[\x9d\xb1m\xd9\xf2\x99\x97\xca%6\x5c6\x95\
+\x033\xca\xd2\xd1\x9bP\xbd\xaa@\x0d\xac\xc5S_\xdb\
+\x14+\x9b\xea\x94\xa4\xd0\x03)\x93\x8au\x0a+\xe5\x0a\
+\xe3\x22[\x1as\xe0\xcb\x85\xcc\x96\x0d\x96\xad\xa0\xa5\xa5\
+s\x92D\xc7\xb3\xe1\x9cd=F\x07\xb6B\x96\x8e:\
+\x99J\xf5U50`d.\xf4-\xdf\x1b\xa0\xe5 \
+7\xa3eM\x92)\x95T\xf9Tp\xd22\xa5\xe6\xa7\
+\xf5\xc3\x96\xf1\xa6\x0f\x05M\xd6V\xfc\x8aX\xaa\xad5\
+\xa5\xb0\x0e\x8d\xa5\x1al'W\x8bj\x80\x1c+\xb1\xd0\
+\x12\xb1r\xd9W\xd2,(jm8\x12I\x16\x1a0\
+R,\x06`\xe5\xbe\xc6\x0c\xa6\xec\x83\xe9\x9aOe\x1b\
+\xc9\xe2\x895z\xd7@g\xc8cyL\x03h\xac\xae\
+\x14st\xe2\xb5e}\xc7\xc5\x88\xe9\xb1(\xaf\xfb\xd8\
+u\x8a\x06\xd3\xb3c.\xc9 \xafC`i3x\x5c\
+k'\xc5\xa0\x0c\xd2\xa0\x95\xc3d\x11\xd8\x02,c\xfa\
+,\x0e\xe9\x8ae\xce\xd8\xc7\xe1\xe1|0Hy\x8aI\
+\xba\xe2LY\xc48\x5cK\xd9\xae6F\xe6\xe6\x8e<\
+\x93M\xa7\xadLn\x00}9\xd9J\x8eR\x8eF\xc6\
+\x05-Gh\x83\x915\xcaYc\xa9l\x18[\xc6H\
+\xc3\x16\x01\xb2g\x9dd\x8cJl\xec\x94\x85\xd7N\x19\
+\xd1\x15\xcfU@\xa1(\x1b\x99\x09\x92\xb6\x02A\xf4\xa5\
+o\xd8\xe2\x81\x86\x17duY\x1f\x08|\xf5\xdb\xdd;\
+HX\x04lt\x84\x11\xdb\xd7c\xe0\xab\xfbW\x98Z\
+\xa9\x7f\xda\xbf\xdd,\x8d\xcd]2.\x97\x0bD\xa7o\
+\xcb\xe2d\x8f\x91EL\x1a\x12\xe4x\xcf\x14\x93/\x02\
+G\xef\xd7\xcd\xec\xb4\xf7|o]\xb7$\xaf\xd5\xcd\x9e\
+\xc2\xfe\xbd\xd3\x22I\xee\x8b\xda\x99\xef\xb6\xe2v\xa2[\
+\xa7\xdd/\xccn\xa7=\xb6\xfb\xa0vv\xefa\xce\xb1\
+o,\xad\xb82\x1a\x87\x0c\xe3\x0e\xb4{Qs\x9b\xad\
+T\x19E\x99C\xa4\xa6\x89U!\x0d\xa15\xe4\x80\xb3\
+Q5,WC\xb3T\xd1\x0f\xda\x18\x8fO\x0e4Q\
+\x8b#\xf5\x01\xb1\xaaOS\xe6\x10\x9d\xa0\x81b{*\
+%\x19d\xb7eM\xf6\xf4\xa9\xa8\x81\xa5^\x86r\xce\
+\x1e6z\xac\x06i\xab\x06\xb6\xa8\x81;P\x03\x95\x8a\
+\x1e\xc4\xa2\x07\xa6\xb8\x1e\xd1\x0a\x9d\xad\xd8e\xd1T\x93\
+r\xd9\x984|8P\x98\x19<\xf9\x08>\xfe\xdb\xe7\
+\xfb\xb7\x90\xdf\xb5c\x19\x05\xcb\x0b\xa2\xe7\x89\x1e\x5c\xa7\
+\x98\xc6\x1d*\xc7\xdb\xe5&\xf2^\x97\x1f\x991t\xcc\
+\xc8<}7\xa3\xd6\x813fG\x9f\x91\xbdT\x81\x91\
+\x9eqJ\xc3ma\xca\x88\x19C\xe6\xb5g\xfa\xdfj\
+\x14\xfeO\x0ff\x8f]\xb3\xfb\xdd\xec\x16gJ\x8f\x96\
+\xe1\x85'\xf4\x83F\xd9b\xff\x96\x8e\x10\xbe\x8b\xdeH\
+\x9cb\xa23\xf2\x86g8\x88\xf0/\x121B\xdc\x06\
+\x09\x06\x02\x19ME0}\x0e\xa5\x0bC\xff>\x87\x1a\
+\x8c\x82\xf3\x83\x17\xed\x07b\x87Ae\xae2\x15\xbb\x17\
+\xbfc*v\xd7\xdc5\xe7z\xc5\xd8\x95\xe1F4r\
+(\xf8\x06\x845\xa2\x02\x8c\xe8\x98\x13\x9f\xf2Xr\xad\
+\xb1\x10\x046\xc41\xc4/]\xfd\xf5\xf7\xf7\x1a\xb6h\
+1\x1c\xeb\x81\xd7\x88\xa1\xcd|\x89\xef\x03\xafq|\x04\
+\xc9\xde\x1d\x81\x92\x97Y)\x84\x7f\xdc#\xd09\x13r\
+\x12\x06a\x16>h\x80n\x8d\x80\x9e\x10w\x12\x19\x06\
+}\x11=LDh\xf4\x07g\xf0@1S\x87b\xa6\
+\x9dY\xc0\x04\xb4\xc3\xba\x93\x98\xf4\xa4Dz\xba\xae\xe4\
+\xe9^\x8c\xf1\x8c\x8c\x8e\xfbc\x14A\x80Sa\xb7\xbf\
+\xc8\xfd\x95 \x9b<\xa31Eh\x81k C+v\
+\xf7\xc5\xb0\x84\x00\xc1Y\xfa\xc8\xfdY+\xcc\x89\xf88\
+\xec\xcf%aK|\xbbH\xf3w\xdc\x9fC\xd0O\xc2\
+S^q\xba\x94\x03\xcf\x04\xee\xea\x8cM\xc6Y\xa0\x0a\
+M\x95\xf7\x8f\x89\xab\x84L\x83\xa5d\x84\x7f\x09\xda\xf7\
+py\x9e\xf3d2\x9b\xb5\xe2\x91w\xe4\x0e\xa2\xe1\x04\
+!@c\xcf2\x93Q\xf0\xf9\x9c%$\xde7I\xa6\
+\xf4r\xf0\x11\x94\xe3\x151\x8c`r\xc0\xd0\xf9\x02\xa1\
+&WpYh\x06:b\xffF\xfc\xb3\xc1\xe6aA\
+G\x0aR\x00Dg\xd2\x0b\x1c-;\x97*\xb6!5\
+)\x9c!\x08\x0c@%\xc3\x87i>\xcc\x05?07\
+\xe0\x17\x9c\x95\xf0\x04\xc8`\xa1Z\xf4\xc3$\x9ah\x06\
+izD?r\x87~\xf0E\x8b\xe8\x87\xf5\x0c\x03|\
+O\x96\x0c\xc9\x84e\xc9\x5c4\x16\xb3\x04x\x08iD\
+(K\xc6-\xc7\x86\x0d\xdc 8\x22N>\x80\x22\xc6\
+\xaf\x7f\xd2\x85u\xa2\x1f\xc4\x88\x83\xc5\xca\x10\xb5X\x8c\
+\xd4<\x1e\x1fM\xa1\xb2\x95\xce\xd4l\xe1\xae.\xb6\xba\
+wW\xdc\xfb\xf3\x99|\xda\xd1\xc9;tx~0X\
+\xb1c\x9e\xe3=\xc6m\xb6\x8c;\x09\xe3\xe6\x91\xa1k\
+\xc0\xfc\x08h\x1a\x86\xc4\x16\xf5\x03\xe7\x0a\x16\xf1\xa0\x05\
+'\x8b\xf4@\xc3\x17\xe0\xc8\xa3O2\x91\x05\xd2JK\
+\xbcT\xd4>[;\x1b\xd9J\xa2\x13\x90\x01?\xc4`\
+\xeay\x06\xc4\xd2A\x10\xae-\x14\x02\xb2\xd5VG\xef\
+\xd1m't{w\xfd8\xdb\xb6ml[\x1f\xb1m\
+\xdf\xca\xb6\x93\x90mq)\xb0\x02\x92mK\xb2\x8d)\
+\xee\xc8\xb69 \xdbF\xc8v\xea&\xdb\xae\x90mw\
+L\xb6\xedC\xb2]\x88h\xa0\x15\x16\xae\x8d\x14\xaa\x92\
+\xa4I\x88\xb3P\x02\x9dDKBv\x845\x96\xf4\xcc\
+\x8aqm\xf33{\x94\x9f\xb9\xa3\xfcL\x17;-\xe9\
+Y>L\xcf\xf2Qz\xe6w\xe9Y7\x9b1\xaa\xdd\
+,\x83R;\xc5CL(\xc9c\x14\x8e\xdf\x92=\x1e\
+\xaf\xce\x1c\xae\xae-y\xf4\xf7\x92G\x98\x94\x11,\x18\
+\xd3<\xb3<\xce\x07\x97\xb7\xa3\xbb_\xc5\xc7aP\x9d\
+\x09s\x86\x86\xf8V\xfb\xd4\xad\xc8\xe8\x1dK\x87If\
+/\xf6\x00\x05\x05\x99I\xf2\x92\xdf\xd0\x86\xa0u\x88\xc2\
+\xe4\x92\x89\xd1U3\xec\xea\x82\xa1\xa5\x85\xc9\xef.\xb0\
+O\xac\x03^\xd4I#\xe7\xd4\x92\xd3\xc2\x1fIJK\
+t\xa1\x86x\xa4%\x95df\x9c\xe9wH\xa4\x8bu\
+\xdc\x19\x87\xde\xba\xb2\x80\xb5\xc0\xb6aQx\x0e\xd5\xc5\
+J}\x9f6\x9aJ\x8d\x00\x9a\xc4J\x81~,\xb0\x1b\
+\xdd\xa1!f\x17\xc8\xa8\xb7\x9a\xf6n\x8d\x13\xb7\x10\xc5\
+\xb38\xfaQ\xfe\xa0\xb0/o\xae\x98\xf1\x1a#\xfe\xdd\
+f#\x09\x8a5\x0c\xf3\xd6\x97\xd4\x11\x81\x86G\x8bt\
+\x16\xff\x92\x1e\xc2\xf9\x821\xd7\xd9\xd0a\x04\xc5s.\
+\xe0\x0cv\xe8\xf0\xc7|\xe5t\x09:\xdcr\x12\x06\xe0\
+9Lr\x09#\x19\xc9B\x14\x93\xa9\xdan6\xc9C\
+\xa4\xaf\x96\x22\x83\x22?+\x13\x0e\xca\x8c\x03N\x99\x1e\
+\xb3\x1c\xd3\x85K\xda\xd1@@\x00?\x98%\xbd0\xa2\
++L\xe5J\x86\xa4%\xef\x22\x8da:G\xf7\xa8x\
+\xe9%\xb3\x8b\xc2\x88\xb0\x0fF\x8f\xac\xa9\xb9\x81{c\
+F\xa1\x99\xa9\x90\xd616{\xe6- \xfa\xec\x17\x94\
+(\x1e\x9c\xbd\xc4\x1cdw\xc0=\x1b)\x12\xc0\xbf\xa0\
+c\x8c\xc2\x908S\x02\xdc\xd8\xb2\xc7\x13\x83FrV\
+\x042\xc2\xd1\xc0\x8b\xddFU\x0c\x5cf\xabM>n\
+\xb4YLz\x90\xb0\x18\xdb\x05S\xde\xaa\x0f\x0c`\x1b\
+:\x02\x9d3+<\xd0\x1emS\x091\xa4\x04^\x5c\
+;\x0e\x93\xe6\x16$[\x04mB\xf2D{S\xe4{\
+8X6r\xa1R5\xa9\x0b6o\xa9\xfe\xcc\xa4i\
+\x898M\xe8\xbf\x96\xc8\x94@\xadZ\xb5I\x17m\x8a\
+$\x97\x89>\xc4\xe7c\x1d|%\x18\x97\xd0\x19\xc5\xb6\
+\x99R\x8am\xe7b\xdbZK,5\xce\x1co\xed>\
+G\xec\x8e\xfa\xc1\xaa\xbd\x0aYn%q\x01\xe2\xaf2\
+Uhg1$\x1e\xf87Gz<\xe7\xb71\x96a\
+\x95\xac\x1a\x9e\x80!\xdb\x98\x5c\xd4\x8e\x8b%\xa7\x82\xd3\
+\xa0\xcd\x97@h\xa8\x9f\x06X{:\x08Q\xd6H\x92\
+c\xb9IM\xd51)\x14\x85\xd5\xb1\x14\x8e8\x83)\
+\xf1I\xea\x02\xb6D\x1dV\x94\xc4\xd0@\xc0%\xdd\xf7\
+\xf6\xbe\xf5<\xba\xe5\xb8\xf3&q\xc7?\x22]\x01Z\
+\xe2L\xa2\xff\xa3\xc8/\x09%\xd3S0J\xab\xda\xf3\
+r\xfbP\xcd]\x87\x9a\xbb]\xfa\xe3\x88L\x9f?\xb8\
+U\xe2\xbd9E\x99a\xeb\x8e\xa4<\x93K\xe8\x8e\xe4\
+0\x11\xfa5(K\xdb\xd1r-\xf5>J\x09f\xcb\
+\xad\xb6Q\xde\x89u\x22K\x90m\xc4\x12c\x8d\x94\x1a\
+t\xa1OY<\x1e\x94(J\xadH~z\xa9AZ\
+\x19o\x09\x18l-}\xdd\xb4\x8c\xc8\xb2\xa0\x09\xad\xc2\
+\xee\xdb\x91\x8d\xf23^\xf3\x10\xe0\x8eBO\xf0\xe6\x0e\
+`\xfe\x84\x94\x9a\xab\xc4\xa9s\xaa2\xd3vWq\xbb\
++b\xe2\x85\x85\x96\x1c\x5c\x09O,+\x1cl76\
+\xd8\xee\x8c\xd5\xbdR\xd2\x84\xd1\x0ddH\xbf\x8c\x97\xb8\
+\x11\xa5\x1c\xad\xc4\x07\xd1\xe3\x84,X\xc2\xb3\xc4\xe0\x91\
+\xfd\xe3\x9ea\x5c\x93$\xba\x92\xdf\xfbjj\xb0\xda\x9d\
+\xdfW\xca\xf0\x095\xbd\xe5\x80?'y\xb2\x12\x1f\x14\
+\x97V\x8b\xdbw\xabe\x81\xb9\x5c\x07o{\xd5\xe5j\
+\xbe\xe4\xcf\x0a\x1cy\x89!{\x80\xbb\x00EL\xf0\x0e\
+V8\x1e\xdf6DV\xb6\x09\x8b\xa6#\xa1\x1a::\
+o[\xdd\xf3\x07\x08\xbb\xd3q~dJVDwS\
+\x92\x88\x82xB\xaeC\x98\xb6\xf0l6\xf4\x1d\xcf\xc0\
+\xc9\xfb\x0e\xe4\x1c\xaeT\xac\xbbcvG\x91\x22\x04}\
+\xc7v5\x18\xbcEz`K\xf0\xd3z[\xe1\xad\x06\
+\xfcU2\xcf\xa1\x10af`\xd8q\xa9\x93\x07z\xc5\
+\xe8\x8bGQN\x18X\x96\xc3@\xdcU\xb4QO\x13\
+9nt\xbb\xc7\x13\xbe\x06{\xf1?\xc6\xc8&\x02\xb0\
+5\x00\x00\
+\x00\x001:\
+\x1f\
+\x8b\x08\x00\x00\x00\x00\x00\x00\x00\xec}ms\x1bG\x92\
+\xe6\xe7\x9d_\xc1\xd3|\x19E\x00\xc5|\x7f\xd1\x8cw\
+c\xd6\xde\x99\xd8\x08O\xec\xc6\xcd\xee\xde\xde'\x07\x04\
+\x822ohRGR~\x99_\x7f\xdd\x00\xc9j\x08\
+j4\x80\x06d\xc9g\x94)\x03\xd5\xd9Y\x95Oe\
+eeeWU\xff\xe1\x9f~\xfc\xee\xfa\xec\xfb\xc5\xdd\
+\xfd\xd5\xed\xcd\x17/\xb0\xc0\x8b\xb3\xc5\xcd\xfc\xf6\xe2\xea\
+\xe6\xcd\x17/\xfe\xf3?\xfe4\x8d\x17g\xf7\x0f\xb3\x9b\
+\x8b\xd9\xf5\xed\xcd\xe2\x8b\x177\xb7/\xfe\xe9\x1f\x7f\xf3\
+\x87\xff1\x9d\x9e\xfdyq\xb3\xb8\x9b=\xdc\xde\xbd:\
+\xfb\xe3\xc5\xed\xeb\xc5\xd9\xbf^_\xbf\xbb\x7fXf\x9d\
+!\x15(09\xfb\xeb\x7f\xfd\xf9\xec_~|{{\
+\xf7p\xf6\xef\xd7\xef\xdeL\xff\xf5\xe6\xac,3\xffk\
+U\xe6\xab3+\x00g\xff\xfc\xee\xea\xfa\xe2LQ$\
+^\x9e\x9dM\xa7M\x11\xf7\xdf\xbf\xf9\xcd\xd9\xd9YS\
+\xbf\x9b\xfbW7\xf7_\xbc\xf8\xf6\xe1\xe1\xed\xab\xf3\xf3\
+\x9b\xfb2k\x8b+\xf3\xdb\xef\xce\xff:\xfb~\xf1\xa7\
+\xdb\xbb\xff\xb5x}\x8e\x05\xce_\xd4;f\x1f\xbea\
+Y\xd3\xa6\xfc\xff\xbaZ\xfc\xb0\xb8\xfb\x97\x1f\x1f\x167\
+m5\xee\xcfy\xfd\xf6\xab-\xb7w\xe4<GX\xbf\
+\xefb\xfe|\xe3\xdbww\xd7\xe5\xf6\xee\xcd\xf9\xc5\xfc\
+|q\xbd\xf8nq\xf3p\xdf\xd4\x12\xbb\xe4\xf3J>\
+\xbf[\xcc\x1e\xae\xbe_4\xc5|\xd7T\xa8\xbd\xb3)\
+\xfb\xb7\x1d\xe2\xbb\x8b\xcbg\xea\x1f~\xf8\xa1\xfc\xc0K\
+\x22\xcc\xccs\xa0s\xa2iC1\xbd\xff\xe9\xe6a\xf6\
+\xe3t\xfd\xd6\x06\xcc\x0f\xddJ\x00p\xde\x5c\xab\x94\xbb\
+Q\xbd\xfa\xf1\xfa\xea\xe6o\xbd\x95Y^\xed\x96\xde\xe8\
+\xd3\xdb\xe6\xef\xf9\x86\xa7\x8cr\x7f\xfb\xeen\xbe\xb8l\
+\xee\x5c\x94\x9b\xc5\xc3\xf9W\xff\xf1\xd5\xf3\xc5)\x94\x8b\
+\x87\x8b\x0e\x9b\x86\xe9\xfd|\xf6v\xb1V\xeeS\xe6\x0a\
+\xaf\xd9w\x8b\xfb\xb7\xb3\xf9\xe2\xfe\xfc)\xbf\xbd\xbf\xab\
+\xe0\xb8\xcc\xb8\xba\xf8\xe2\xc5\xd7\xb3\x9f\x16w\xdf\xac~\
+\xffpu\xf1\xf0ms\x99b\xf9\xf3\xdb\xc5\xd5\x9bo\
+\x1f\xea\xef\xef\x1bm\xf9\xe7\xdb\x1f\xbfx\x01gp\x86\
+\x14gO\x17n\x1b\xce\x97\xd7\xb7?|\xf1\xe2\xfb\xab\
+\xfb\xab\xd7\xd7\xab\xe2\x167\xb3\xe6\xeb\xf4\xf5l\xfe\xb7\
+7w\xb7\xefn\x9a\xc2n\x16?\x9cun~\x92\xea\
+U[\xdbF\xa0\xb7w\x8b\xfb\xc5\xdd\xf7\xab\xdb\x9f\x00\
+x\xf5\x5cm(L\xcbKORu/\x89\xad\xdfu\
+q;oph\x98\xbe\xb9\x9d6|\xbf\xbf\xba}w\
+?};k\x10n\xda\xf0\xef\x1b\xb4\xafg\xf7\x0d\xed\
+\xf9\x7f\xde7\x1c\xcf/f\xdf_]\x9c\x7f\xb5\xb8\xff\
+\xdb\xc3\xed\xdb\xf3\xfb\xa6\xdf\xbf\xbe\xfdq\xbd\xe4\xdbw\
+\x0fo\xdf=|\xb3h\xbb\xce\xaa\x0a\x0d\xf2\xb5\x19V\
+\x97\x97E=g\xae3X,\xcd\xc1\xf4\xf2\xeaz\xb1\
+\xaa\xe7\xf9\xb7\xb7\xdf-\xce\xdf^\xdd4\xb0\xdf\xdd6\
+_\xe6\xf7\xe7\xb7?\xfe\xf4fqs\xde\xdcq\xddb\
+y>\x9b?,\xbb\xe9\x86Loo\xde|\x90\xfd\x8f\
+\x17o\x1b}3/\xfa\xc1\xcb?\xd5\xcb\xff\xf8\x87\x8b\
+\xc5\xe5\xfd\x93V\xb4\xdf\x11\xda\xdcF\x89\x17\xb3\xbb?\
+\xdf\xcd.\xae\x9a\xae\xbb\xc6d~{}\xbd\x987\x0a\
+2\xbb\xfea\xf6\xd3\xfd\xb3J\xad\xdf\xc2\x8e\xde\xf0\xb9\
+o\xa0l\x08Z\xd8\x1f~\xban\xe4m3\xa6\x0d\x8b\
+\xc6r\xfe\xf6r\xf9\xf9\xfd2\xeb\xb6\xd1\x85\xab\x87\x9f\
+^\xe1\xef_\xac\xe8o//\xef\x17M)\xd0\xfe~\
+,\xa2\xa5d\xc7|qv~\x00kx\x9f5n\xb0\
+&\x5c\xb2>_\x97\xe5\x1f\xff\xf0,\xfc\xdbFQ\xde\
+.\xe6\xad\xadZ\xd3\xa5\x87\x9f\xda\xae\xb9N\xc6\x17\xef\
+\xa9\xed\xdbo\x9a^4U8k\x0c?\xb4\xff\xe2\x06\
+\xc1O\x8d\xc0\xed\x05X^\x87\x8d\xeb\x7f\xff\xe2\x85\xc3\
+&\x83\xf5b\xa7\xb7wWo\xae\x1a\xe5\xe4%\x91T\
+\xd2\xa5\xa8\x1d\x19\x08\xa9\x11\xf8\x97+\x1f\x9a.\xe5\xfb\
+nv\xf7\xb7\xc5\xdd\x1a\xaf\xa6\xc1\xe7\x7fk\xc9\xffx\
+ww\xfb\x03\xfeeq\xb3\x12\xa7\xe1\xbd\xb8i\xb5\xfb\
+\xdd\xc3\xed2\xe3nq\xf9\xbf[S\x03O\xbf\xfe\xbb\
+\xfez\xff\xfe\xaa\x8cO\xc6\xf1\xd5\xa3m\xfc}\xd3\x19\
+\xde\xce\x1e\xbe\xad\x1a\xd7\xfebV\x7fT\xc2&\xe7/\
+g\xad\xdb\xd0\xfc\x9d}}\xa6\xcd\xb7\xa9.\xbfN\x91\
+\x8av\xb2\x97\xb9\xcf\xa4\x7f?{\xb1\xd6\x0b\x1a\xd3r\
+=\xbd{w\xdd\xf4\xf6\xef\x177\xb7\x17\x17M'\xb8\
+\xbb\xfd\xdb\xe2\xd5oa\xf9y\xfc9]Z\xfdWX\
+\xe0\xed\xc3\xefW\xf0L\x1b_\xe7\xee\xe1\xd5M\xe3\xeb\
+<\xf5\x94f\xa0\xbf\xb9oF\xa8\xef\xbex\xd1Z\xa3\
+\xc5\xef\xa0\xc8\xcb\xb3\xbb\xdb\x87\xd9\xc3\xe2w\x18\xf0r\
+Eq\xbd\xfc\x09\x13x\xb9\xea>+~;\xc1\xbe,\
+s\x04\xf0\x95C/\xf4}\xc8\xcb\xa7\x88\xfc\x16\xe0G\
+\x83\xfd\xd7\xd1`\xff\xf5P\xb0\x0d>/\xb0\xa9\x0b\xb6\
+\x1d\x80\xf5\xd7#\xed\xc9\xd7\x87\xda\x13\xfc\xcc\xecI\xf4\
+\xda\x13*z\x08\xf0\xa3\x95\xfc\xebC\x95\x5c\xe2\xf3R\
+\xf2\x18\x80\xfb\xa8~\xc1rP\x97C\x9d\x02\xa4\xa8\xf7\
+o\xf5\x09\x964B\xc5\xda\x8f\xf7:\x06\xe1\xad\xa4\xe3\
+\xfd\x5cC\xe4\x01?\xb76\xd6>~\xae!\xb6\xbe\xcb\
+\xfe\xac\x87\xfd\x5cC\xf4\x0f\xfb\xb9\xed\x97\xd9\xf5\xcep\
+,\xe7\xba\xaf\xbe\xbd[4s\xf3\xdf\xae\xf3b\x02\x7f\
+\x86l\x9d-Q\xe8\xf2\xd2\x9b\xc7\x8c\xff\xbc\xb9zh\
+&\xdf\xef\x9aY\xd8_\xdb)\xe1\xbf\xdd4\x13\xb25\
+\x8a\xff\xa8\xca\xfb\xdd\xec\xe1\xee\xea\xc7\xdfa\x01!R\
+\x99@\x9b\x0a\x92\x01\xca\x84\xb0h\xb6_\xa6A\xc5\xd0\
+\x13_.\xf9\xcc\x1b\x05\x94,\x80\x98\x8e\xab\x9cF\xe9\
+\xa8\x98\x0b\x83\xc72\xe7r\x83\xe6r\x83\xe6\xae\x9dE\
+;\x89ar\x8f\xfel\xaa\x08\x11\xd3\x90\x8a\xbc\x96\x14\
+\x9a\xed\xa9\x22D,\xc3*\xb2\x90\x8b\xf9<\xdfc=\
+\xa0!Dl\x1f\xd6\x90\xfa{\x84\x86TX\xfa\x10\x0b\
+\x1e\xad!P<\x125\x97\x1a\xf2\xfc\x83\xb0\x80\x85\xca\
+\x84\x8b\xa4\x13\xc5bJ/WU\xc5e\xfb\xa32\xac\
+\x0a\xff\xa9\xc9p-\x8c\xc0\x18+\x12\xfa\xe2EP\x91\
+\x10\x13Z\xd1P\x87fg\x8d`b}\xb1\x15\xbb\xda\
+\xa6\xb5Q\x98\xd874b\xdf\xc9\xf5\xd9y\x0f\xeb\xdc\
+P\x89='\xd7\xa7\xd3\x97\x0aZ\xbf\xbe\xe0\x11,\x0a\
+\x22\x09\xe7\x04\x0b\xb0\x0a\xdbd\xda~C3\x9e\xb4\xd7\
+L\xc1'\xa6E\x1d\x94'S\xc9\x82B(\x1d\xe5\x89\
+\x22\x19j\xf4\xac=P\x02QA\xd2\x9e\xd5G\xa28\
+\x93\x13?\xab\x0f\x156u\xb2\xdcC}\xe0D\xb1\x15\
+\x82\x1db+\xa4m\xda7\xb6B\x88?\xeb\x98\xe3y\
+\x04\x0dQL\xafcNH\x0aMPJ@B\xa3\x12\
+\xee\x05Q\xfdS\x18s\x98HO\xa4\x22\xe4\xc3*2\
+[\xb4i\x805n\xb2\xce\xd3\xab\x08i\xbf\x8a\xf8)\
+\x8c\x08\x14\x8e0\x8f\x09\x14!0\x91\x89AA\x08\x9a\
+L\x85\x0b1\xa8\xad+\x0cd\x84<+L\x14Ie\
+$\xaa\x0a\xe3\x85\x1d\x22\xe2Ya\xac\xb8\x03Z\xfa\x93\
+\xc2pIW\x10\xd0\xdd\x15\x86I?<\xea0\x9dl\
+\xd4a:\xe1\xa83,1\xf0!]\x04\x8a\x05\x8b2\
+\xd9`_a\xd0S\x85\xaa\x19N\xef\xc23p\x7f_\
+9\x86\x83\x96\x99\xd9:\xeb^B\xd0\xc4\x16S\x9eh\
+13\xf0\x5cLeB\x05$\x18\x9a|j\x07c\x12\
+\x22\x96\x96\xc8\x8b\x06\xb2z\xed8,\xc5\xdc\x8d\xf3\xb9\
+\xe3x\x11#r[\xe5\x5cn\xd0\x5cn\xd0\xdc-\xfb\
+\x96\xa6b\xb6\xe0\x1e\xd5y\x01\x8d^\xe7\xc5\xf1\x08X\
+\x86\x9a\x02\xb5\x8en\xa6\xae\xec\x8e\xa7\x19\xb5Y\xa1i\
+,\x13D-\x8a\xc4\x93)aI\x16\xcf\xea\xbc \x16\
+&\xe3\x90g\xe7E\xbdppf>\xfb.(E\x92\
+\x95qEC\x95f\x0f\xdf\x054>lg\xc0`w\
+;\x933\xb7\xcd)\xf5\x0e\x93!\x9e]\x0cO\xc5\x0b\
+$%\x01\xdb\x8b\x8d:Z\x9f-\x03\x87\x0d6\x0a\xc2\
+\xe8[m\xda\xfc\x82\xe6\xbb\x9aK\xb0\xd8(\x22Q\x1d\
+9\xf4\xc5\x80\xc8\xbb\x97A\xbb\x9b\xe4\xd7\x19=\xacO\
+j\x97@\xa3\xd7.\xd9x7O\x0b\x99\xb3`ku\
+\xb4\x80\xa2\xa1<Z P\xf7\x94\xd5t\x80\xda\x1e\x15\
+\x5c\xdc\x15'S\xb3\xa2\x1c\x1a\x1d\x8bd\xc5\x5cM\xe2\
+\xd9\x22\xa5\x16\xb7$\xaf\x16\x09\xad\x10:\xab\xd4\xa1\x5c\
+\x0a\x0aR`\xc7\x22\xb9\x98\xc0\x1e\xf1\x06\xcd\x0fw1\
+2\xd8\xa3\x8bU7\x7f\xb8\x8b\x0d\xdf\xb7\xd97^l\
+T\xae\xafo\x91\xc1\xa6\xe2\xbb\x8e\xabz\x97\xfb\x1e*\
+\x0f\x00\xf0\xf1U\x9e4\xb7\xa8\xfc1\xdcV\x95P\x9e\
+`\x11KDY\x8e\x10\xa0h1!*\x16\x9a1A\
+r*)\xed\xb4\x98\x12\x8b\x00U]G-N\x0e\xee\
+\xcf\xba\xaeQ \xd2\xc2\xab\xae\xaf\xd1\x5cn\xd0\xdc-\
+'>(\x9c\xa6{\xc4\xd6\xd4O\x14[\xd3<Ul\
+\xcd\xf0\xe4\xfaB\xea\xfd\xfar\x8c\xe8+1\x1a\xda\x04\
+\x0b%\x0aa\xa3\x14X\x1c\x96YX\xd0\xa35\x8e\x82\
+X\xc2\xa5\xb9\xc6\xa4E\xb5\xa30B\xc5\xd0\x95\xeb\xc4\
+\x18\x09\x8a\x09>\xcdj6h.\xbb4u\x9e#`\
+\x11\xfbD\xde\x12\x07\xf4\xe5\xf5u\xb3ti\xa3I\x07\
+&\xc5\xcb\xa9\xf9\xde|apF\x9c\xba\xeb\x04\xa7u\
+\xc7\xe0\xd9U\xa3\xe8\xfaq\xfa\xec\xe6Q\xec\xd8\xf6\x9b\
+\xd0a\x985\xe3\xcao\xfe\xe1\x1f\xfe\xe1Q\xceZQ\
+\x0ck\xdd\x9f\xde\x09\xce|\xfe\xc18C\x05\xb4\x91\xb2\
+\x9f3\xf5p\xfe\xe1\xdb\xab\x87\xc5\xe6\xc4\xa9\x0b\xe8#\
+\xdfO\x03@M\xf3>\x005-\x07\xc4<\x08>M\
+\xc7\x13\xc3W\xfb\xda\x7f\xff\xe5\xeb\x7f\xfd\xea\x1b\xfaf\
+7\x88*\x9e\x15\xe7.\xfe\xb5Q\xaal\xdd\xda\xf5*\
+\xdc\x9f\xfe\xf4/\x7f\x04x\xb1\x86\x03m@T\x85\xec\
+e\xf3\xe5\x97\xef\xb3\x91\x8a\xc8\x11\x03\xdd+\xdc\xf2\x9b\
+\xbe\x19\x22'\xee\x01iJa\xe1\x8a+\x02\x15\x16\xf1\
+\xfa\x08\xc3\x8a\xb2\xda3\xc0)E\xf9qf3^\x0c\
+\x84-r\xf0>rhG\x04^\xab}\x98\xae\xd5>\
+L\x8fU{\xdcR{=B\xed\xbdPr\xad\xbc\xb6\
+?\x8fUw\xdaRw\xdf\xa7\xee\xd6\xa9\xbb\xd4\xbaG\
+\x01\x00\xaa\x95\xb7\xd5\xef\xb3\xf3\xea\xbb\x1c^{\xff\xa6\
+\xcfc\x91]<\x96\xea\x8b\x02U\xa7\x02\xa90\xc8\xb3\
+\x83\x89\x9cE5\xf4HU\x8e-U\xf6#W9W\
+\x96\xe7\x0f\x8d\x1f\xd6,\xd0\xbe\xf9\xb7\xd7\xff\xa7\xa9\xe0\
+\x92f\xf1\x7f\xdf]\xdd-.\xeaz\xfe\xfd\x97\xef?\
+\x19\xd3\xa6|X[\x13\xbe\xbe\x22\xfcY\xda\xb5J\xac\
+\xc6\xb4?\x5c\xbdz\xfb\xe6\xf2\x7f..7\xb0ZV\
+\xe1\x9b\xabZ\xf27\x0d\xe1\xea\x9e\xf3\xa7\x9bZ\xc9\xce\
+\xd7\xb869\xbf9\xa5QEb\x87\xcf\xdb\xaaVA\
+\xe8s4\xab\xb5\xfa\xf2\xf9\xd9\xd5Zy;\xada\x1d\
+_\xfd\xad\xb5\x8f#\xfbjG\x8e\x9b\x13C\xf6\xd5^\
+0\xf6\xe9\xc0S\x97\x12\x04\x0e\xdei\x03(A\xecu\
+u\xc7\x14\xa3 a\xd6\x8eP\x89>\xaeh\xf9y\x8a\
+Vg:\xfd\xb3 \xe7}\x94\xce\x8bc\xba\xd4\x9e\xe3\
+%\x02k\xf7\xd9 ir\x18\x8a\x08bh_\x1c\xa3\
+\xb3\xee\xb1\x88\x83\xa0-\xa6\xed\xf2\xc7\x8f\xd8\xc6\x9a\xee\
+\x1f\xb5\x8d\xab\xcfs\xfc6^\xe7\x8b\x9aa\xcf\xde\x0d\
+YI\x95\xae\x8f\xc3X@\x14TW\x91\x9d\x0d\x9a\xcb\
+\x0d\x9a\xc6\x11\x8a\x92\x16\xa8<\xbc\xecK%=&S\
+*\xa9\xc9\xb1\x8c\xe3\xc3\x04J\x82\xba\xd3\xc4\x8b)\x90\
+\xeb\x04\xa1\x84\x07\xf8\xcb\xe1V8\xb9%\x0eS?\x81\
+%n}\xac\xf5\x92z\xcb\x97\xfc\x04g\xeda\x8a#\
+'\xee\x95\x13\xf7\xcc\xdd\xdb\xbc\xc6\xfd<\xa2\xf9\x0b\xb3\
+\xe8oi\x97}\xba=gQF\xaa^O\xed\xee\xf5\
+b} \x8b\x05\xc3\xf5\xf4\xfa\xea\x22'\xd0\xd7\x8f\xd6\
+\x08.v\xeaF8\xf9$\xd34\xe4\x18\x8b0<\x9c\
+\xf8iQ[\x066\x89&Y\xd8\xd0\x81p\x82\x05\x8d\
+\x9b$/\xf7\x9b\xaf\x9e:,`\x1a~\xb8\xf8\xd5\x01\
+\x88f\xd8\x1f\x19<\x18\xaf\xa8=2\xa6\x12\xd4\x07\xca\
+P\x00\x019\xeb8*\xc5\x15P\xea\x03\xe5u\x9a\xcb\
+\x0d\x9aF\x0c\x82\xe2\x81\xa4\x03\x8a\x81K\x85\xc0B\x89\
+\x86\xd0\xaa\xc7\x14\xb5\xa8'\xc7\x0e\x03\xe6\xc9cB\xf4\
+y6\xfep\xe4\x88\x22?\xedN}\xdaP7\xfdB\
+B\xdd\xf4Y\x87\xba\xe93\x0eu\xd3\x09C\xdd\xa7\xef\
+\xfdN'\xeb\xfdS\x8cb\x1c\x946\x99Z\x09\xb66\
+}b\x83:\x91\xeb1\xec\xfa\x14\xb3d\xfb\x89\xc94\
+\x0e\xb0\xf0\xcd\xce\xcb\x87\xc5\xddN\xfb\x01W\xa4L\x82\
+/\x9a\xfb\x16\x7f\x9e\xbd\xbb\xbf\xbf\x9a\xdd\xfc\xf3\xf5\xbb\
+\xf6\xfe!\x0e\xed\x04\xe6\xe2\xab\xe6P\x8f\xd9\xc3\xd3)\
+D\x9c\xa2(\x9d\xa5\x00\xebL\x99\x84W\x8b\x02V%\
+\x9f\xb4=\x04\xc4\x8e\xd1\x1eT\x04\xda\x0f.\xa6\xb6\xef\
+\x90{\xe4\x89\x81!r\xfff\xce\xacs\x16.\x12n\
+T7\x83\x89\x17\xc8\x08\xaf\xc3\x0b\x16SO\xcb:\xc0\
+P\x16$V\xb2\x83\x82\x0a\x838\x1a\x97\x94\xb4hA\
+|.s\x8aE@\xdc$\xaa\xfd*\xc4a\x18\xf9\x5c\
+\xf9)r\x09\x93Lz\x92\xb0\x12\x1d\xbc\xa4\x81)c\
+x\xcbE\xc4\xf6\xcdn\x9b8\xd4\xe0\x95\x97T\xc9:\
+\xa88\x171\xb2\xe8\x0c9^\xda\xdf\x18U.(,\
+\xe68\xbcf\x9a\x99\xf9\xd9NF\xfbI\x9c`\x16\x17\
+\x0f\xe3\x09c\x81\x0ce\x7fy0BD\xe0\x83\x081\
+\xf1\xe1\x08e\x09I\xb0:\xa7\x86\xce,\xb5\xa0)@\
+G\xa9a\x08\x94\x09\x17~\x84\x85\x8aj\xf2r\xa7\x0a\
+\x14\xe3\x0c\xf2\xc9\x14-\x0b1:N,\x8b)X\xe6\
+(td\x18\x1d\x94C\xd1!-\x1e\x86V\xf5\x87\xb3\
+\x90\x05V\xfd\xa94\x9d\xb5\xf0\x5cP\x9dx\x87\xe81\
+\x96\x5c~&p8\x0a\xac\xca\xc3\x91\xe4\xccOEG\
+\xaa\x86@\xd5\x8c\xaa-K\xc5\xe0@\xb4Gm\xc1D\
+\x1e\x85\x0e\x8e\xd0\x91O\xcb\xc6$\x1a\x89\xc6D\xa0X\
+\xfb\xd1\x09S\x01\x0b\x03\x1a\x83\x90\xe4)m\x0cS\x85\
+F\x0a30Q\x1dW\xb4\x04\x03!Y\xed?@\x85\
+\x11I\x86W\x1d;\x0a\x85-\xc1\xa1\xe5\x06JQe\
+\xf5\xc9\x94d\x14\x1e\xbe\x83\xc6\xc0\xc1V\xc5W\x9d\xbe\
+\xc2\x82$\x05\x12\xb5\xc2\x92\xb9\xa4\x09\xad\xdb\x83\xa98\
+\x85\xb1\x1c4,M\xa6\xba\xeau\xab^\xb5\xca\xb5Q\
+\x18\xe9\xa0\xcd\x11\xc6C1R\xc9\x02\x86,\x1d\xcb\x9b\
+\xc5]\xac3\xe1B\x93b\x96Us*\xcdv\x8c\xb2\
+8\x9aj\xe4bJ\x8f0!\x03br;\xb5a0\
+\xb4\xf4F\x9f\xb0(\xb0\xf28\x9c\x06m\xb3\x0b\xd1\xbe\
+8\x1d{d9\xd2\x88\xd7\x8b\x02\xca\x98\x11\xea\x97o\
+\x83IYF\xd8\xe0_\xf8\x18N\xca\xe3\xc7\xf0}\xf6\
+2s\xdf^f\xdd}\x03\xd4\xdc\xe7\x17\x17\xbe\xeb\xc6\
+9\xd1\x18\xb7\x97\x19\xc7\x9f\xa0\xd1\x07\x07\xcai\xce\x02\
+`\xb4\x93\xedo\xc68\x1d\x1cp*8\xe8tp\xc8\
+\xe1;\xdd]\x88>\xd8\x1f\x5cHv\xef\x0f\xaf\xa5I\
+\xb6\xff\x86\xc0\x9c\xe7|\x86{o\x08T\x96\x18f>\
+\xbfh:)\xeey\xd2\x84\x0b\xd9\xe1p\x920~\x10\
+N\x12\xe6\xe3\x1c\x950^7\x8b\x90\xa6\x83\xe7\x8b\x8d\
+:\xea\x0ee`\x9b\x86\x9b,\x92<p\xb3\x08\xef\xdb\
+k)\x9c\x1b\x5c\x02\x08\xd3c\x8b\xb9\x5c\xcc\xda\xb4\xeb\
+~N\x11\xdc\xdd\x12_\x5c6i\x03\xcd\x11\xca\xc1\x99\
+C\xd6\x05\xd2b\xb1\xafu!\x01\xdc\xe5\xe0\x95y\xb3\
+\xdbj\xcf\xee@\x02<\xde\xd8\x0e\xfb\xc8}\x06y\xc4\
+\xb3\xdc\x913\x82\x1fq\x87y\x09ui6\xe67\xc7\
+^1(\x8c\xfd8\xe50N\xc7\x9c]n\xce]7\
+\xe7\xb7}s\xe0\xee<\xf9\xc8\x181#\xf4b\x944\
+\x8c\xd1\xd8\xb8\xc4f\x9cc3\x18\xb2\x191\xa9\xe1\x94\
+=\xb6\xabF\x1crP\x5c\xee\xb1\xcf\xdf0./h\
+\xe7\x83\xe2r\x8f\xcd\xf23\x99\xc3B\xd6Y\xefs\xc2\
+\xfa\xe6\xb9\x9d,\xa4{\x9c\x09>t\xbe\xf8\xf0Y\xa4\
+\xc3'\x9c\x0f\x1f\x87z\xf4E\xc5\x99}\xea\xef \xa3\
+\xd5\x7f\xfa\xb8\xf4g\x82$\x935u\x87\x02\x14\x9a5\
+\x90\x00\x051\xa0\x1b\xe4\x89\x02\xc6\x0a\xfe\xa4\xed\x95\xe6\
+\xc0\xe7\xaa\xeet\x8c\xe7\xaaP\x14\xc0\x98d\xcb\x83U\
+w\xe9>X\xed\x9b\x8a\x9b\x15\x22\x11\xad\x8f\x01\x95\x0a\
+a\x88\xd5\x85(\xc6\x059\x14\xb5\xa2\x97V\xc4Bl\
+\x87\x87\x0a\x1c\xc5\xc3%&SX\xb1\xf1\x1c\x11\xc1\x22\
+\xb1\xe1gt\x06\x87G&\xac\x08\xb9V\x93\x87h\xc5\
+1\xc4\xab]\x84\xc2\x86\x12\xa4V\xa37\xbe\xc2#~\
+\x06@\xe4\x94\x80\x84.\xf1\xd0|\x06Dr\xa9\x1fN\
+\xd5\xc5\xb0G9\xea\x10\xeb%\xad\xa5\xd9\x0d\x0e\x9d\xc0\
+(\x04h\x18\x01\xa0C\x110-\x22\x81HU%\x9c\
+K\x068V\x04\x88\x8bk\x98\xd4\x1e\xe2Y\xd0\x89d\
+\x8c\x5c0,W\xea\xcf$\xd7p\xcb\xa6\x8fkV\x8e\
+\xd1\xe2\xf7\xac\xf0\xb9k\xac\x9c\xd5s\xd3\xac\x1e\xeb\xf2\
+\xa8\xb1\xf3\x9a=\xaf\xd9;,0E\x0eN\xca\xe5\x02\
+S(\x8a\x1e\xea\xfb`\xb0^W&\xb6\xbeU.\xf2\
+\xcdx\xc9\x91\xb0\x88\x10r\x05 \xa2D\xa0\xf3'\x06\
+\xc3\xa0\x8d33;\xf8\xf1\x8e\x17\xf2$\xae=\x81\xc8\
+\x8au|\x03\xe5\xe2!\xa8\x5cc\xd2\xc4\x85\xc9\x91V\
+>\xd3\xdb\xbb\xc5\xec\xe2/\x8b\x87oo\xdb\xda/.\
+\xdb\x92w\xc2\x0aJ\x18\x82\x18\xb7HEAC\x8e\xe6\
+k\x116\x0c\xf3Q\xfd\x87N:RZq\x05\xef\x0c\
+\x0c\x1e%9\x99+l(\x85\x5c#\xaa\xf9p)\xae\
+\xfd\x90\x8d\x90\x96\xf2\x84\xd6\xc2\xa0\x9eif\x85E\x14\
+\xeav/\xa7\x02\x04\x81\xb5\xcf\xac\xd3\xcc\xbb4C\xee\
+*\x14\x0d4\x0a\x9d`qs7oc\x00T,\xc3\
+HV\x01\x81\x12\xe1\x91\xd1d\xb3\x16\xf0@\x9bhI\
+\xc1\x84\x941\xbd\x8c|\x10B\x91\xfc\xb5\x97u\x84\xa3\
+ \x1b\xe9|\x8c\xefeZ\x98M\xa5\xd3\xcb\xb8\xb8\x90\
+q\x9c\xa6\xa7\xf1.ve|O\x0b+A\x96V{\
+\xda\x14\xad\x98\x85D=\x86p\x9dh\xbeF4\xdc\xd7\
+H\xc3\x0c\xbc\xf5\xc4\x95\x90\xc4\xa5\xfd\x1a\xe4\xc2\x22\x13\
+(\x82`\x808a\xa0\xa2\x9c\xa2\x13\x85\x02\xaa\x9a:\
+\xaa\xa3\xe1I;ZA\xd5\xd4\xe88v \x85t\xcd\
+\xabK\xb0\xe0\xce|N\x8a\x0e\xc1\xf5~Wb\xd7#\
+\x8dT8h\xbbM\xe0H\xcfS)s\xf0\x89\xd9\x85\
+\xb7i\xdf\x98v&\x1d\xe9\x81\x07n\xb2\x1e\xf1\xc4\x8c\
+\x89}Pb]\xd8\xde\xcf\x08)\xf0dGB\x93\xe0\
+\xe1\x12\x9b\xb9\x0cI\x0c\x80&\xb1\xa7\xc4fn\xc3\x12\
+#\xcero\x89\xcd<\xc6HlV%^ck\xb1\
+\xc7I\xc4\x97s\xbc\xa4\xfd\x1f\xe3i\xf8\xe2\xf5\xfe\xc7\
+\xa42\x06W\xe6\xef\xc3\x01{\x1cd:\xa7\x99n4\
+\xe6\x188e\x97\xe3\xd4/.\xe6{+\x90\xec\xd4e\
+.._\xef\xaf@\xe2c$f\x1c\x92\xd8\x81\x1d`\
+o\x89\x99\x87%~}\xa9\x0b\x80\xbd%f\x1d!\xb1\
+\x88\x9dd\xe9\x84\x89\xc4\x89\xcc\xa2\x89\xc2\x18\x89a\xd8\
+,\x06.x\xb1\xb7\xc4`\xc3\x12s\xf8\xc5\xc6\x03\x94\
+a\x89!N}\xc0\xac\x99Y\xdf\x9ee;\xca#8\
+NSR\x9eX\xc9`\x14\xc5\xf6\xdc\xff\xa9\x17\x0f\x94\
+\xb4\xf6\x07\x16U\xc3\xe0\xd6\xa5\x22*A\x868\x99Z\
+\x16\xcb0\xb4\x97\x1f\xf0\xe4\xdf\xce.\xbaG\xd0:\x00\
+kg\x03\x12p\xd1\x80\x1a\xdaY'\xb9|\x9f\xe4n\
+I\xa1\x9a\x12#v\x94\xf6;o}*\x89P\x1f^\
+\xd7\x90rD\x9d%J\xdd\x1b\xb4K8\xf5\xe8\x95\x17\
+\xeb\xad\xbcR\x9d\xaab\x81\x94\xcc\xc7\xda\xaer\x100\
+S\xaa\x1f\xae\x05#\x91\xb9\xfbT\x19\xc0Cw\x91\xab\
+\x7f.7\xbc\x83\xd0\x8b\x86CZ\xb4Z\xc7\x01&J\
+/?&R&\xc7\xd8y\x07E\x90\x12\x98&P\x0c\
+\x93U\xe2e?.{\xb7\x0dYD\xac\xb5\x8d\x03d\
+~L\x9c\x1c>u\x9c\xb8\xa4!H\x85)\x0a\xb1\xa6\
+\xf8G\x85\x89~\x85\xa9\xe3\xb8\xf5\xfat\xf5\x91\x9cj\
+\xe1V\xd6\xba\x03\xb4>\xfa\xd9\xb8\xbaZ\xea\x91\xc8(\
+\xfe\xf1-\xae\x09\xf7J$uY\xb8Q\x01\x0eG\xea\
+F\xc2\x12\xd9\xf1Q\xac\x0d\x92VP(N\x91\xa1G\
+\xd1\x9f\x09\x1f\xdd\x88\x9aK\xaf\xf0\x01\x9d\x88M\x814\
+w\xad\xd1S(\x1a`\xe2U\x07\xb3$\x0bs]\xe8\
+\x15Q\x8cY\xe2\x93\x1aF)p\x9f\xf3\x8b\xac\xb3B\
+\x89\xac$\x9b\xad\xafP\x12q7\xaa2S\x01\xcaP\
+\xf9x\x1211\x1e\xc7>\x91\x11\x1b\xe5\x04\xeb\xca\x15\
+\xcdB\xa4\x01\xde=C0\x9d\x09k\xcbW\x9a'\x14\
+\xc4\x0aQ\x84\xe6GE\x81\x8f\x84\x023Q\x8bBI\
+TH\xe9\xa0\x81\xa8\xc5\xcd<*\x1aX $\xa1z\
+\x90\x8c\x85;4\xb4\xb2\xe0\x8c\xa4G\x06\x83\x89\xbd\x17\
+\x0c\xe1z\x04\x97AIQR\xac\xa6\x0b\xb4\x04\x83\xd6\
+\x11\xa4!\xaa:<\x8c\xe3\xc7\xb3C\xe4\x01\xfb\xf4W\
+(\x02\x04V-\x95I\x017\x94\xfaP\xa3\xd2TW\
+\x1fJ\x1a\x11\xca\x91O\xa8\xa2L\xe8=\xe8\x1a\xf2\x08\
+\x0b\xce\x0a\x82@\x84N\xa0\x04\x05\x98\xf9rS\x92\x89\
+KZ\xfb5\xc2\x98\xc9'\x98XD\x8cxbTH\
+9\xc1\x86\xdc\x8fv\xaeg\x5c@\xd0]\xeb3z)\
+\xe9)\x10\xf5\x99$\x17U$\xac\xcf\xfa\x15\x8b&e\
+\xd6w1q\x11\x177Z\x02<\xbf\xbez\xfb\xef\x8f\
+\xaf\x81\x7f\xfa\xbe=\x88\xffD\xc5HY\xdf!\xbf\xfe\
+\xbe\xf7W\xef\xee\xae\x7f\xf7\xdbu\x94\x19\x99_\xfe\xbe\
+\xbdZ\xa3\x00\x8f\xef~_\xbd^\x7f\xfd=\xf0V$\
+\x15\x00B\x9e.\xb4\xed\xd9\xb4\xfc\xab\xbb\xdbw7\x17\
+\xdd\xcc\xffs{u\xb3\x9e\xfb\xddU\xb3\xf4\xed\xfa\xaa\
+\xf9\xdf\xab\xe7\xdb/f\xf7\xdf\xce\xee\xeef?=\x96\
+VsW1\x88WRP\x1f\xb3k\x0d\xbb/\xc3\x9f\
+bHq6\xd1\x09yIa\xd7\xb3/\x9b\xdc\x94\x92\
+\xaen\xcf\xb9x6%\xd0\xd2\xfc\x99O\x18K`\xa0\
+[\x93\x89T\xd2\x81l\xc2Y4\xc8\x8dZ\x06DP\
+\xd2]b\x22^42\x93\xdbL)\xa1\xa9\x13\x8dB\
+D\x86\x8fy\x06I>\xb1(\xee)\x90g_w\xb3\
+\xd3J\x10\x81`\xcb\xb6f#@\x09g\x8f6\x13\x0b\
+\xabx\x9b)\x05)\xa8\xcdD/\x0cM\xaa\x99-_\
+T*h\x19]\xdaV^\x89\x82\x18\xc8]\x16(R\
+\xc2\xd8\xb8\x16\xd6\xcd\xac\x15\xfb\xba\x9b]\xc5\xf8r\x95\
+m\xc8\xf2,1\xad\xca\x92T\xc1.4\xa8V\x14\xc8\
+d\x1dF4)dN\xf4\x0c96\x99.E\xd2\x91\
+\x9f\x1aG\xf2\x83\xed\xf8\xf7\xb3\xb5\xf6\x15.\x12\x08(\
+K\xc6\x1e\x85\xd3!\x9f\xb3\xb5\xcd\xa4\x02\x12\xe8\x13\xd1\
+\x92\xe8-[\xf3\x82\x1e\x81\x13\x85\xe2\xe1\xa4\xb6\xaa\x17\
+\x156\xe7\x9c\xa8\x16\xcd\xb0hE\xc8\x92\x94\x0d\xa9a\
+\x09f\x0eZe\xb6w-\x9b\x17\x12\x80\x96p\x11d\
+Q'\xc0N\xf6\x97\x8f\xd9(n\xcf,\xb0\xcd\xf4\x82\
+\xa0\xfaT\x94g\x9bG\x85R \xde\xabVz\x11\x12\
+\xa7G\x01\x22\xb5\xcd\xc4\x02\xae\xf1,)\x7f\x18\x95\xbf\
+w\xc2u\xad\x05`\x5cy>\xddu\xc47\xb7\x17\x8b\
+v-qcJ\xe6\xf7\xf3\xe5\xe7\xfe\xf1\xbfUd\xef\
+\xc9\x92\x9c\xdc\xccW\x034\xde\xccC\x914\x04\xb5\x09\
+\x14\x11\xc8\xa0lm\xbb\xb3\x81\xa6L\xa6O\xc6\xdf&\
+\xd3\xcc\x82\x82\xe2\x93Xzj\xa0\xb1\x8b\x99w/\xca\
+\xcc\x8e\xef\x9by\xac\xeb\xd5\x22\x0a\x08\x05\xe4\xc70\xf3\
+\xac\xd5\xcc\xef\xd8\xbe\x9b\xda\xe1k\x864\xbd0a\xe0\
+\xba\x1d\x05/j\x88\xd4\xb5\xa3\x88^<\x11\xb8kG\
+\x91\xb4\xa8\xaa\xd1{\x06\x80\xb9\xa8Q\xc8\x9a\xb1`/\
+\x22\x12V\x0d\xe9*\x13S\x8c\xd7\x0ci\xcd\xee\x1a\xd2\
+\x9a\xbdn\xdb\x98K\x0a\xbcg\x05)KDp\xae\x1b\
+R\x93\xe2\xaa\xc4\xbanH\x0d\x8a\x03Kj\x97\x85\xb6\
+\xd5\x8dL\xad\x85u3\xbb\x86\xb4f\xaf\x1bRm\xab\
+\x1bn\xda\xb5\xa4\x86\x05\x02\xdc\xb5\x0b\x8ee\x81\x08\x00\
+\x5c\x07\xd2\xad\x84\x92\x06v-ix\x81 I\xaf\x96\
+\xb46\xe4\x9a!\xad\xd9\xebv4\xa1d\x90ht\xed\
+hH1fe\xadv\xd4\xb3\xb8\x09\xba\xad\x1b,\x97\
+\x92J\xae\x1d3\xeaT\x14\x15=\xab\x19]f\x8az\
+\xb2\xac\x9bQ$*\xa8\xce\xde5\xa3\x8f\xd9\x00H]\
+3\x8a\x98\xc5\x12\xc2\xbav\x14QJx\x8a\xbcgG\
+\x11\x0a @\xac\xd9Q\xe0b\x86h\xd5\x90VT\xd6\
+\xec\xe8\xae\xbeS~~\xbe\xd3\xcfe\xe5\xf3\xa3Zy\
+\xc4\xe2d\xc1\xfa\xf3\x9a\xf9Op%;\x11\xd9\xff\xf7\
+K\xb9\x8d\xf2\x98K\xb9\xc7\x9f\xaa\x99O[K\xd3I\
+'\xc8\x5c\x00\x80&\x04%\xdc\xa8\x95t\x85\xae\x16\xec\
+\xbc\xf6w\x0a\x85P\xa2>x\x8cg\x81\x9f\x84\xe9y\
+#vP\xff\xfa\x0d\xa7\xaf\x90\xbf\xdax\xca\xdc\xf7Z\
+\xe7\x90~V\xac\x7f\xfc\x12\xbf|\x9fUaU\xee\xe7\
+g\xfd\xfc\xe0O\xa9\x10\x1b\xfc\x0cS\xfa\xf9\xc5\x16~\
+\x10_\x02l\xf0s\xf5~~\x09\xdb\xf8\xf9\x1f7\xf8\
+=\x9a\xdc\xaa4\xfb\xac\x16\x03\xea\xd9\x88\x09\xb2\xc7y\
+#\x17\x8b\xc5\xd0\x81\x00]\xd6\xb6\xc7.wZ\xc4\xe2\
+bc\xe5\xc4\x88\xf5q\x06\x87\xecK5:\xee\xf9\x08\
+]\xd62\xe2\xf8\x95\xd1p\xa4\xf6H\x9c\xbe\xbb\xc4\xc6\
+\x8b\xdcC\xe2\xcc\x9fQb\x91<@\x01Dq\x10\x8e\
+\xe1Wm\xf7\xb0\xe6qo\xf1\x86\xdf\x8f\xda\x99L\xce\
+\xb2}g\xb2I\xbb1\x99\x8a\xb5\x1f\xef\xdb\x9e\x8c\x14\
+\xcd%\x93Cw'C\xbd\xfb\xa0\xad\xc9u\x1b#\x14\
+$P\xab\x11g.\xa8\x09\xack\xcfp\x1d\xa1z>\
+\x95fp}\x0f(\xb7\x9f\xa7\x01\x950St\x82X\
+\xdc&R2I\xc8\xc6\x9c,\xca1\xbc\x97\xc7\x0f?\
+\x03P\xa2\x10\x12*\xd6G&Z\xd4\xa2\x0b\x8feI\
+b\x88X\xdbe\xc0I\xf6)\xc0\x03\xa3\xe1\xf9\x05+\
+\x8f\xc7 :lx\xb0\xf2H\xa1\xf0.<\x89m\xad\
+\x1d\xac\xbb\xd4\xa0\xbev\xba\xbd\xee\xa1$;l\x96&\
+\x98\xc0(\xd1mPt\x01\xf8e\x8a.#D\x1f\x9c\
+s\xd5m:\x89%@\xa13]U/\xa0\xa1(u\
+\x02\x96\x05T\x03\xf2y\xde\xa0\x5c\x8c\x81\x86gcE\
+1Sr\xe2\x85\x15\x938\x17\xd3\x98L\xa3\x801\x09\
+k\xfb\x0b\x0b\xb9\x13\xa2L\xa6\x04\x85H\x9c\xac\xf9\xca\
+%\xd3\x8c\xf3\xf0I\x1b\xb1\xd3p\xd7\x918\x10D.\
+\xea\x81\xa4uo\xa1\x14e$\xad\x93X\xec\x92\xcc\xdf\
+'\x99wI\x06\x80\xb4\xa2\xe0\x19\x96\x8f\xd3[EH\
+\xf4\xc9\xd4\xb8\xb4\xe0\x19N\xa6L%\x1d\x10u\x14b\
+'5\xc5i\x85\x8dA;+\x97\xa2\x18\x18v\xb6\x11\
+2\x15P\x0c\xab\xbdN\xb8x&\x90\x1dh\x8b\xbd\xa8\
+\x98\x99\xf9q\xec\xb1\xed\xb2\xb1]\x0eV*T\xeb\xe8\
+\x14Z\x81\xb0\xaaR\x8e\xc5]\x89\xean\xd5.\xc5\xbc\
+K1\x1cD0\x85@i\x90\x22\xc3TX\x9e\xad\x9e\
+\x04\x98!\x13.\x9c@\x086A/\x0c\x09\xd6\x5cV\
+/\x96L\xe3:\xa5\x0d\x1bu\x8b<\xd8\xa8\xb7pd\
+pU\xb1\xb4\xc2\xceH\xdc\x89\x92A\x11\xf2\xea\x0cU\
+\x9aA\x15CwR\x89\xc7n\x08\xee\x84\xe1\x13+\xaa\
+J)6\x99JIAs\xc0Q:6l\xfd\x15\xf1\
+\x97\xa3c\xccG\xd61\x1a\xd61;X\xc7(J\x0a\
+%\xd55Jb\x85HS\xde\x8b\xc4rx5c\x8e\
+\xc5\xc8\x04\xe4@\x1d\xc3\x12b\xe4\xca\xc7\xd21\x18\xd6\
+1\xe0_\x07\xc7\x8a\x98\xf0.\x83\xe3\xaf\x96\xbf\x17?\
+\x1ck\xf9\x7f\xf9\xbd\x92c\x97^\xf9\xab\xe5\xef\xd31\
+\xd6SX\xfecz\x05G\xf2T\xc6\xe8\x18\x8d\xf4.\
+~\xf9N>\xed\xe4\xe4\xff:\xfd\xde\xda\x17IFL\
+\xbf\x07\xfa\xe2p\xe8e|\x94g\x8c\xfe\xe0\x88\xf0\xcd\
+\xe7-:\xc6IE\x1f\x1f\x86\x1d\x1f\x14\x1e\x83\x8e\x8e\
+\x8a\xe6\x9e\x1e\x9d\xf1O\x14F\xc13\xe2I\xc9/_\
+y O\x81\xce\xcfi.8$N\x19\xe3w-N\
+\xe1\xdc\xd9V\x07Q\x1cD\xb8\xcadU *\x0e\x80\
+\xa6c\x04\xb2a\x81\x92\x0f\x15\x88\xb2h0j\xe7\xdd\
+\xc6Y \x08\xbd6\x92[1[?!\x0a\xad\xa0\x90\
+\xd1\x18\xb9v\x18\xcd\xc3\x0e\x95+l\xe9\xe8zV\xe5\
+\x83\x22\x18\xa8TMNafJ\xc4G\xb9*\xcd(\
+\xb9h\x84E\xfeT;\x15|\x9c\xa7G\xc1E\x11C\
+\xad\xbbu\x89\x04\x11\xb3\xba\xafV\xc8\x05\xb53\x89\x84\
+\xa2\x16.8|\xf2\x06QF\xda\xd3\xfb\xff\x1d\xc0\x01\
+'S\xa2\x92\x14\x99\xb6\xfc\x8a\x9e\x181bq_p\
+\xfc\xfa\x9chO\xc4\xec\x84\xa10)\x9a\xec\x150\x91\
+\x82\x99a^\xb5\x8c:$\xf3\x0d\x92y\x87d\xf8%\
+\x14f@*\x13(\xda\xa8S\xd1 \x01\xf5\x89\x14q\
+\x0a\xf5\x98 Iaso\xb0\xc3\xc8Bi\xce\xa3\xb0\
+\x93a\xec\x98\x0f\xc4\xceKZt\xb0\xe3(\x22l\xdc\
+\x85\xae*\xda\xfa\xe5y\xbd<\xd4/\x059Z@\xa0\
+\xa0\x01\x9bf\x03\x9f\x00'!O\xb8\xb0\x87\xa1\xb5!\
+1(\x98,\x18\xcb\xd7\x9d \xaa\xe68\xb5\xa3a\xbb\
+F\xf6iE.\xba\x07\xb9\xc8\xa83\xb5\x83a\xe7\xa8\
+\xc4x\xcd\x89,\x8a\xeeI]\xd5\x09\x85\xc8\xba\xd8}\
+\x9df\xde\xa5\x19\xd4!\xcaTamu\xc8\x15(1\
+&X@\xdd\x89\xad\xf9\x16L\xc0m\x97\xc3\xa5M\x83\
+\x9cL)\x8a\x90\x1b\xe0\x18\x15\xa2\x1d\xfcM\x8e_-\
+\xd7\x07\xb1\xb3],\xd7\xaf\x0f@z\xf1\x93]\x1e\x80\
+\xfc\x8a_/~\xf4\x09/\x1d\x18a\xd7\x09v\x09\xc8\
+\xff\xaa\x17}z\x811\xe2\xa1\xcfg\x88\x9f\x1e\x1b?\
+\xfb\x84\x97K\x9c\xde\xadB9\xce3\xd7\xfaF\xa9\xee\
+K\xa3\x1e\x8d\xd6\xb7\x8b\xab7\xdf6\xf4X4\x83L\
+Wp>\xee\xc9\xca\xc4\xe7-t\xcb\xdd\x9f-]\x02\
+'\x03\xae\xea\xb6\xa4\x13\xc54\x05\xdfZ\x95\x8dWT\
+\xf5\xbfd\x8a\xe4\x83/\xa8\xc2\xc2\xe2\xa6\xabh\xd1V\
+\xa1\xeb\x1b\xaaz\xa4\xb7\xc0\xf7\xa4G\xf5\x14\xeaJ\x0f\
+\x1e\xe9(\x86\xeb\xe2\x93\x88\x07u\xc5G\x22N<\x9a\
+\xf8\x16\xdc\xf3~.&L\x94\xf1\xd2+\xf3\x91\xea\xaa\
+\xac=M\x05\x06\xc0\xaa\xe3+\xcbiG\xaa,g\xf4\
+\x00+\xb9_E\xd7\x0d\xd5\x90\xf5\xc4`\xc3U\xb0h\
+\x8aE\x8c\xd2|1]\xd9P\xa3e\x1c\xc4\x0bi\xec\
+\xea\x04\xd7\xa3\x0f\x88R\xbf\xc1o\x9e\xc7\x80\xa9\x81G\
+a\xad\xd3j\xb6,JBO\x1b/I\xb9\x00F\xdd\
+\x18\x5c\xefy\x1e\x17\xc8\xb4p\xf6\xbc\xc17F\xeeN\
+\xea\xd9\x9c\x84p\xf8N\xad\xcaw\xf6\xea\xbb\xab\x8b\x7f\
+o6\x9d?\xfc\xb5\xb9\xb8gE\x879\x14\x1d\xc1c\
+P\xac?-?;\xed\xb1\x1c\x1eyB&Ss\xa9\
+\x0f\x87\xcc\xa5\x86\x0f8\x8a;s\xd4\xd0|\xd6\xa0|\
+\xbdz\xf0\x13\xa1D\xf9\xa0\xf2P\xa2\xf5\x03@\xd6\xa6\
+\xa1S\xa2{\x14\x88\x12\xa3\x9f5c\x9b\x86X\x17D\
+\x92^\xfe\xb4E\x83\xe4\xa2M\x83\xfc\xd9E\x01\x00\xfb\
+\xcb\xa0\xfe2\x14\xda4X\x86\x10Cff\x7f\x19\xb2\
+\xa5\x0co\xd3p\x19l\xb1\xbd\x8c-\xcdl\xd2\xa6\xe1\
+2$\x07\xe4\xd8\xd2\xde\xaem\x1a.\xc3@{\xf9\xf3\
+\x96\xf6\x8eY\x9b\x86\xf9;\xe2V\x19xK{\xcf\xb8\
+M\xc3e\x04\xcaV\x9d\xe2\xfe\xf6\xbe\xbf\xba\xfe~q\
+7\x5cD\x22n/\xc2\x06_\x1f>\x5c\x86\xf0v\xa8\
+b\x0bT\x97M\x1a.C\xd1%<\xa2\xb7\x0c\xd9\xd2\
+\xe4)m\x1a.\x83I\xb7\xca!\xb4Em\xe7m\x1a\
+.C\x153s\x8b\x1c\xb2\xa5\xfbi\x9b\x86\xcb\x08Q\
+\x01\xf3\xfe2\xb6\xb4\xb9R\x9b\x06\xcb0\x08\x03\x00\xea\
+/cK\x9b\x0b\xb7i\xb8\x0cA\xdb\xaa\xbb\x8a\xfde\
+p\xb6i\xb8\x8c\xa4\xd0 \xea/\x83\xb7\x94\xc1\xc3B\
+8\xa7omp\xd5\xd1c_dDfz\x7f\x19\xbe\
+\xa5\x03\xda,fC\x9d\xa3\xe7\xdd;\xe3\x0e!\x19\xf6\
+~\xeeV\xaf\xa4T1\xeb\xac]\xa4\x92ikk\x17\
+\xa3\xb8\x93R\xdf\xb2\xc1p\xfb\xb0O\x13\x1e[\xac_\
+\xb6\xe9P\x9f&b\xdba\x18\xde\xa6\xc1v\x05I\xe9\
+\xe7O\xfd\xfc_g\x9b\x06\xf9#H\xf6\xf3\x97-:\
+\xb3h\xd30\x7fa\xc2$\xee/\xc3\xb6\x94\x91M\x1a\
+\x96\x81P\xad\x9f\x7fl\xd3\xfb6\x0d\xf3\xb7\xb4m\x83\
+B\xe4\x96v6o\xd3\xb0o\x99\xb1\x85?\x1d\x81\x7f\
+n\xf5]#\xb7\xf9\x95\xd2\xa4]|\xbe\xe0\xed8m\
+ik\xc16\x0d\x97\x91\xaa\xdb\xcb\xd86\x8f\xd06\xed\
+\xe0\x04dDj\xbf-\x05\x18m\xaf\xd5!M\x89\xfb\
+\xcb\xa0-e,\xda4\x5cFJ\xf4\xf3\xdf\xd6\xde\x97\
+m\xdaap\xd6\xed\x0e\x00lio\x876\x0d\x97\x91\
+\xbc\xdd\x07\x87\xd8R\xc6\xeb6\xed0>kl\x1d\x9f\
+\x11\xb6\x94q\xd9\xa6\xe12\x06lH\x22\x8d\xd4)<\
+U\xec\xc1#\x0b\x12\x81o\x89@\x98{\x09w\xc0c\
+\xc6!\xc2\xa8g\xcc6\x19=\x99\x07\xd82h\x98\x8d\
+6\xb8\xc8\x12\xfd\xfcc\xcb\xc0Mm\xdaa\xe2\xc8\xfd\
+\xc6\xdca\xcb\xa07\x1b\x9e\xf9\x1a\xe5\xd6\x1e\x11N[\
+\x0a\xc06\x0d{\xacJ\xb1uD\xf2-\x8d\x9c\x17m\
+\xfa\x84=V+@,u\xe9\x9dbA`\xa5\xf7<\
+V\xe6\xe8\xf5XUz\xb4_m\x8b[\xf9\xbaM\x07\
+{\xac\xbaE1)\xda\xb4\xcb\xe8\xc9\xdbF\x850\x18\
+\xd1\xae\xa3O\xb2K9\xe4\x5c\xb7\xb4\xdd\x03\xe7\xec3\
+\xa1\xf9\xee\xa7\x9c\xc5\xee\xb1\xf3\xd7\x97\x97\xba\x11\x93\x1f\
+q\xc8\x19K\x1c\x80\x06+\xec\x8e\xc6\xbc\xa93~\xa0\
+\xca=\x9ci\x9f3\xce\xe4\xe2\xa8`\x18\x1e\x02\x86\xf1\
+\xee`,..\xebK<\x07U\x83M{\xd0\x18f\
+=\xfe\x00<\x0e;\x04\x8e\x88S\x9d\x80\xc8\x09?\xe7\
+y\x80\x00\x07\xc0!@{h\xc7\xffk\xef\xda\x9a\x9c\
+8\xb2\xf4\xf3\xfaWt\xf0F\xacH\xce\xfd\x82YG\
+@\xdbLL\x84\xbd;\xb1~\xd8G\xa2\x01\xc10\x03\
+\xb4\xa3il\xe3_\xbfu\xaa$\x95J\xa8\xbaQ\xd3\
+=`\x8f%\xe3V~Yu\xf2\x9c\x93\xe7\x96\xa9K\
+\xea\xf3\xe7\x07\xfc\x1e \xc8\x8c:fI_\xa7:\xc8\
+\xae\xa2\x0e:\xc0:\xca\xbf\x0fP\x07\xc3a\xa1\xe3z\
+\xd5\xc1q\x15u\x08\x1c\xa2\x0e\xc2\x03\xd4!\x07ER\
+\xc2\xebUG\xf2U\xd4\x91zS\xb1C\xd2\xffE\xb1\
+c^\xe6\x19])\xf0\xe5\xe7\x92\xebs\xdc]\xef_\
+r\xfe.+|\xc4\xa9\xd2\xc2)\xfc\xf4b\xca\xf8!\
+e\xff\x04\xdbP\xbcJ\x9aU\xe4\x83\x5c\xe5\x04\x9e}\
+\xacm(\xce\xa4\xd9y\xd2\xd7\xe8*fy\x05u\x98\
+\xe3M\xb9\x8a9\x7f\xa9\xaeb\x91\x97\x9e\xce\x0d\xcf\x9f\
+\x07\x1c\xe8*\x96X,\x1fN\x19.s\x15K\xfe\x04\
+\xdbp\xbcJ\xcd\xe1H7e\x1b\x8e\xf2\x19K0g\
+\xbe\x8a:Xo\xaa\x04s\xf6\xc3J\xb0k\xfeIf\
+\x06\xcc\xdf\xe5O2_\xfd\xbc\x8f\xf1\x84\x04\x7f<{\
+\xac\x82_\xcbI\xaew0[\xd6\xa3~\xdc\xe4\xf6\xf8\
+\xa5I\xd8:P\x1d\xa91\x88o\xf6}8\x9bj\xe8\
+5\x89\x18\x17\x88\xe8\xb7\xae\xe3\xc0\x87pbY\xffB\
+A``P}\xab\xa7\x19\x07\xd5\x17a\xad\x05[=\
+\x0f\x93\x7fz\xa2\xe8\xd5\x15\x90\x8fgS\x81\xe7\x01\xe7\
+M\xa64\x96q\xbb\xbad\xe8\x80\xadcq\xc3\x9a\xb2\
+\x8e\x87\xe2\xa6tm\xb3k\x12\x03a^\x8e\xc0C\xe4\
+\xd0-\x11x\xc2}\x98N\xb8\xaf\xf65q\x8f\x17p\
+\xcf\xd7\xc0\xbd7J\x1e\x99\xd7j^\x17\xeft\x01\xef\
+z\x08\xef\xb6\xc5\xbb\x8c\xbcG\x03\x00\x1a\x99\xb7\xbe}\
+\xddg\xc8:\xfa\x9c\x18N\xbca\x92\xbd!b\x1an\
+xUn\xcc$\x80\x1b\x8e\x11\xb0\x83Ds\xc5\xf3x\
+\xcdGL\xe5\x90\xad\x9e-\x9f\xbf\xfd\xe6\xfex\xd6\xdb\
+\xc9\xeb\xe5\xb3\x9f_.\x7f\x99\x88\xf8\xcb\xcb7\xcfN\
+\x7f\xb9\xb3\xfe\x88\xbe\x85\xdf\xda\xd7\xbf\xfe`>\x10\xed\
+d\xb2\x93\x17\xcb\xee`\xa9\xee\x9a\xff\xba\xb5\xa7k\x95\
+A\xfbwr\x06\xbe\xdf\xbd\xec\x8e\x9c;}\xb5\xec\xe2\
+\xdc\xd3eQ\x5cw\x9cuC\xec\xc1O\x9f\xfc\xa3\x9b\
+\x82}=ON\xcf\x9e-\xcf6#\xe0\x04\xee\xb3y\
+7C\xd6?\xfa\x8e\xe2g\x0d\x0fE\xcff\xb2\x9e\x9c\
+\xbc]N\x99\xff\xed\xf4\xb4\x0b\xc1\xd4T\x89\x90u\xda\
+9|~\xbb)\x09\xa4L\xbb*\xdc\x1a5\xf5\x88\xa0\
+\xbd\xaa\xac\xd8l\xb0\xb7\xeb}\x91\xf5\x1d\x82\xef\xce\xce\
+\xbay\xbe\xf3\xea\xe4\xfd\xb2\xe3\xfc\xfb\xfa\xf3x\x95\xc0\
+\xff~\xfaK\xa9\xad\xb2\xe1\xbb\xe5\x08\x95\x8a\xdf\x8e\xe0\
+H\xab\xef\xb9\xf3\xe4\xc9\xe9\xaf\xab\xde\xad\x12\xa6\x08\x8d\
+u\xd3_\xba\xd6\xa3\xb3\xd3\xd7\x7f;[\x82\xd8\x8f\xcb\
+\xf3\xf3\x97o^\x94\x03\xd4c\xa8\x17~}_\xb7\xac\
+\xa0\xa1\x90\xa9\x1a\xe3\xa7_'\xd0\xfbm\xe8m7W\
+\x1d\x9d\xd2^33\xdf\xedx\xffA\xc7z\xbe\x00\x00\
+j\xbezp\xf9\xfa\xa7\xbd\xf8\x96\xb1\xd1x\xe9\x16*\
+#\xba\x1a\xb1\x1bp\x8d\xbd9y\xf2j9\xea\xb2\x1e\
+?\xbf|\xfb\xb2\x03\x07lp\xaa\x0f\xdd\xe9\x9b\xaf\xee\
+\xbf^\x9e\x9f<;9?Y\x9b\xd3\xba\xdd\xed\x18|\
+\xf5\x1f\xf7\xdf\xbc\xbd\xf7\xf6\xf9/\xdd\xab\xe1\xe5\xab\x97\
+O\x97o\xab56{\x17_\x17\xbc\xa5\xc4\xe9\xd7f\
+(\xc6/\xc7\x8c\xcd\xfe\xce\xbf~\xdb\x01\x8c\xe6\xc2\xc4\
+R,\xd60w7\xe3LF\xfd\xf1\xf4\xdd\xd9\xd3\xe5\
+\xc3:)\xee\xed\x15\x86,\xd7\xea\x1c\xf1\xf5\xf7\xcb\xe7\
+\xe7\xff\xd3O\xee\xa8\x98\xd50\xa7?\x9dw\xa7\xce\xfd\
+\xd6\x7f\xe5ce4\xa3\xa8\xe7'g/\x96\xe7k\xb8\
+\xe8M\x91\x92e`\xa3\xfb\x06\xc8\xf2QW\x03\x9dt\
+\xac\xfc\xed\xbf\xffB24J\x9b+Z[h\xddP\
+\xd5`7\xa3\x9d\xa3\x94\xffmfp\xf8&IM\xea\
+\xf3\x93W+\x0f\xef\xee8_\x1e\xaf\xcc\xe7Q\xff\xe8\
+\xf17\xa7?lz\xc6\xeb\xcby\xea,\xbd\x93\xa7#\
+\x95\x9e\x8b\xd2\xf2\x94\x8f\x82\x0b\x1c\x05\xbd\x82\xfchi\
+\xceJ\x8a\x7fDE\x14<o%\xd57x\xca\xfd\xb3\
+g\xcf\xef\xfd\xef\xb7\x8f\xbaCO\x9f\xde\xfb\xbf\xd3\xb3\
+\x7f\x0e\xeeX\xe8\xc9\x93\xd3w\x9d2\xba\xc8\xf5\xec\xe9\
+\xbd\xe7\xc3x/_w\x91\xfd\xee\xdb\x9f_\xfc\xe7\xaf\
+\xaf_\xdd\xbf;v\xd45\x15\xac\xea\xf65\x81\xb3\xe5\
+\xdbr\x83\xce\xa9\xff~~\xfe\xd3\xbd\xbbw\x7f\xea\x0e\
+kl\xa7g/\xba\xfb\xba\xff^\xbf\xac\x1b\xee\xfex\
+\xde\x1d\xd4\xf8\xd7\xa2;\xb8\xfd\x8a\x8f\xee\xd5\x86\xb5\xbb\
+k/\xff\xe6\xab\xafv\x16\x11\xd3c\xd5\xfc\xf1AE\
+\xf9\xe5\xe9\xbd\x14?\xacs\xa7\xab\xe5\xd9]I\xf6o\
+7\xa9\xae\xba\xb0*\xe6\xbdD\x1a\xa0\xe2\x1c%\x0c\xe3\
+\xc8)\xa5\x98\xa7\xa4\x1as\x94T\xe3\xf8AL(\x11\
+\xccSJ\x939J\x91\x0f\x1e\x1d\xf3\x94\x12\xcdRB\
+V\x9f\xa3\xf4\x90\x8f\x8f\xbf\xdd\xe1I\xe6)9\xcfR\
+\xfaV\xbe\xa3\xefrJi^\xe3\x04\x99s\x94\xbe;\
+~D\x8ftJ)\xe6)\x09\xeb\x1c\xa5G\x9d\x9a\x1e\
+M\xad\x80a\x9e\x92\x13\xcdR\xea\x1fSJ\x83\xc6w\
+\xdf\xeb\xbf\xcc1\xe20\xc7\xc8\xeb\xf6\x0ceU\xf5\xa9\
+$>\xe8d\xff\x9c#\x9b\xa1\xe8\xbc-\xc6\x838\x9e\
+\xd0\x13\x5c\xd3\x9b\x99{J\xb3\x88\xf9\xf9\xaf\xe7\x94\xa2\
+\xcf\xcf\x1a\x1b\xcf\xcf\x7f=\xa7\x94\xf2\xba\xe6_q\x96\
+\x922\x1e\xc2\x93\xf2,\xa5\x10\x99\xf7\x93\x87\x1f\xe8I\
+u\x86\x12\xce\x12\xc1\xcemyJ\xc4\xe7\x0c{f+\
+t\xdc\x0f\xb9\xf9M\x8f\xbdj:`\xd6\x8c\x0e\xd5P\
+\xb9\x8b\xf2\x94\x88\xac44\xd1\xc7\xa5\x1aBx|c\
+\xfb)\x9f\xae\x98\xbc\x06\xc58^M1\xf8\xf8\x86\xb6\
+j>Y-n\x87\xaa\x85\x93\x1frL\x89\xc4\xd5\xd4\
+B\x8fol\x17\xe80\xc5D\xc4\x83\x98\xa6\xe4\x98\x0f\
+Z\x00N\xb3\x94\x8e\xe3\xbb\xc8)%\x9d\xa7d>\x1b\
+H\x1f<|p\xfc`\x87\x92_P\x04\x89\xcfQ:\
+\xd6c;\x96)\xa5\x0b\xd2\x04\xa2\xce\x16A\x0f\xbb\xe7\
+4\xb8\xe7|\x9a`@\x9a\x0f\xee\xf5\x9cR\x9a\xd7\xb8\
+\xe0\x05\xa9\xcb\xbb\xa7M)\xcdk\x5c\xcds\x96\xd2\xb7\
+\xf5\x9cR\x9a\xd38\x1e\xe0c\x993\xeeq\xff\xc5\xfa\
+\xb2\x17lR\xbf\x1aP?M0n\xd8T\x8b}\xf3\
+\x8d\xe0\x0e\xf9\xe1\x88\xb9E\x00\x18/\xd0\x1axB\xe0\
+\xd1\xf7\xdb(f\x03\x8b\xf0\x0e\x0dn\x9a\xb6\x86\xd2\xbb\
+c\xf1C\x1a\xd2\x16T\x80\xb1v@\xb4P/@\x1b\
+`\xdf\x96\xa0,2\x88\xde\x92e\x11\xd6\x1c\x1dX:\
+:\xd5\xcf\x90\x85\xb1Pa\x85(-B[0O\x01\
+\x86\x02\x8aR\xdde\x884\xc3\xfc\x88\xee\x1c\xc0\xbfy\
+\xd3\xaeEj$\x08\xf5\xa7\xee\xaf\x7f<`\xf7\x08\xfe\
+ai<\x9c\xd8?\xbcfw\xba\xbd\xde\xea\xda\xf7\x83\
+\xd1\x82f\xe6\xeb\xf3\x1eX\x1d\x85\x17w([j0\
+s\x7f\x9e<\x13*\xcb\x9a\xcc\xb8kt\xfalY\x0b\
+\xcc.v=]=j\xc2\xfb\xc9\xbc\xfc\xd2kbj\
+N_\x0c\x16\x92,\x87\xea\x8b\x04oO\xec\xaeF\x02\
+`\x5c 7\x05a\xb6\x9a\xba\x11%l&l\xd4\xa1\
+edN\x03\xe4\x1a6\xd8\x9d\xc36\x14\xda\x08\x87\xdb\
+\xc4d\x00:\xbc\x07 2\x8a:\x124.\xc3\xf36\
+P)\xc0!\x0b \xc4\xbe\x99\xd4\xdb%\xd0V\x0b\xbd\
+\xb8\x18\xda\x88\xba\x9f\xe3\x11-c\x9b\xf8\x9ci\xca\xc1\
+38z\xacl\xebM\x1b\x97\x08\xf5\x07\xa3\x1b\x7f\xd5\
+F\xc4\x96\x99=\xe2\xc50\x94v\x90\x8e\x8e\xab\xed\xb0\
+ik\x94\xa8C\x0b\x9bG\x16\xa2akD\xb0\x1c+\
+\xa9%\xca\xc2c\xa5\xa8\xe4F\x91\xd5&\xc4j\xe9\xc2\
+{\xb2\x9b\xd7\x83\x8e\x92K\xc1\xba\xcb\xde\xaa\xbd\xeb\x85\
+e5\x83\x91LKg\x22\xd7\xdb\x1f\xa7\xb0\x19\x9di\
+n\xeb,+\x85\xe3\xc2[fh\x19O\x13\x83 Y\
+#\xd1\x80\x90\xdd\x17\xd1\x84\x93\xd9\xb6\x91\x0c\x18D\xf1\
+\x96.@\xb1@\x88\xc6\x8a\x14\x1d\xa9\x09\x18\xc9\xa4E\
+\x1dY\x5c;(Kz\xec\x90Tr\xc5\x0dR\x8a\xb1\
+\x16\x89.\xde\x832\xcc\x13J\xa2\xe2\x80T[\x00\xcd\
+\xa8oSjra\x1a\xec =\x86H\xcc\xa5u\xa8\
+!\x89k~ j\xd2\x0b\xd24\xb2\x9a\x9aH\xb7\x02\
+\xbc\x1a&J\xce\x9b&\x87\x83\xea@\xc3\xccc\x91\x0d\
+P\xb2\xb4\xb4F\xa2\x89sZ\x14@\x94\xcc\x1d\x02\xcc\
+\xc8t\x14\xd9,\x12bD\xbe\x9f*\xfb\x80\x19\xa7\xdb\
+;Q\xa47\x80\x17\x93hv\xd9\x9b\xfa\xeb\xec\x87\xe9\
+<f\xbf\xd1\x10V\x06\xbe\x08[\xd9u\x01f=\xc0\
+le\xbe\xc0\xbaH>\x8aX$w\x17T\x87X\x8f\
+t\xb8T\xa3\x7fe\xd5WS\x8cu\xc7\x91G\xdd_\
+i\xb0\xc8USch\x0e\xfe4\x1dx\xd0\xca4D\
+XA\xfbC.~=\xaam\x9a\xf2\x89\x12\xcbQ>\
+VT\x8f,\xa0\xf7ao\x22\xb2H\xe9E\x95A\xd4\
+H\xef\x11+Q\xfbln\x83\xa8%\x0ch\xddQ\xb2\
+\x11b\xc9V\xe4\xae \xaa\xf3\x15E\xe5CD5\xe5\
+\x02X\xbcD\x0d\xf0Ej/\xaa\x968%\xfc\x80\xd4\
+\xff\xab\xd1\xbf\xb2\xbe\xafD\xad;\xfaY\x15\x1ffU\
+\xf9\x0a\xa2\x06\x5cQT\x9d\x11u\x87\x85\xe3] \xa1\
+\x0dqG\x8f\xb2\xa6\xac.A\xc0\x0e\xcch\xba\xcb\xf0\
+\xae\xe2v\x81\x8cF\xbd\xadG\xactSq\xce:p\
+w\xdcis\x8f&\x10pW\x15\x17\x88\xef\x83\xf8w\
+_\xec\xad`Yp;\xba\xbb-\x08*>\xd7\xdf\xbd\
+\x01\xe7\xcd\xe9\x9b\xe5\x10\x5c\xce\xde\xbdZ\xde[\xfe\xbc\
+\xec\x92H}\xf0\xf3\xec\xf4\x9f\xcbM\x0d34\x87w\
+\xb1\xef\xe1O\xbf\xae\x81\xe2\xae{\xf7\xf3\xde\x93w\xe7\
+\xe7\xdb\xd8?\xba\xdf\x95\xea~_\xaa+nV\xe84\
+t\x15\xf7\x93\x82\xbc\xd6\xfes\xa1\x8c\xfa\xbc\xe0\x0b\xe4\
+\xfa\xab\xd0\x99\xfag\xa8\xf7\xae\xbb>\xfe\xf7]g\x8c\
+\xc6j\xaa\xa3+\xefv\xf9\xbfME|\x03\xcb\x89\xcf\
+\xb4\xfc\xba\xbc\x98as\xbd\xfd\xc7\xaa\xd9\xa7f\x9b7\
+\xa12\xdf\xad\xff\xfe\xac\xe0\xbf\xa8\x0a~\x9c\x7f\xc3\xcb\
+\xe7\xbf\x1e\x9b\x02~\xcc\x81\xc6\xb7>\xbe\xa0\x1f\xed\xe7\
+\xc0\x02\x8a\xcd\xb3w\xc0]\xc6\xf5\xd6\x97\xb9\x1c(M\
+]]\xd8\xc0\xbd\xc2\xfa\xad\xcf\xb1 \xb8qay\xaf\
+\xb0y\xebK\x5c\x12\xcc\x0b{\x81\x80\xbaO@\xc7[\
+\xbf\xcb\x85\xc0\xa4\x8e\xff\xfc\x95\xf9e\x0b\x87\xa9\xcey\
+\x87\xfb\xb1\x93\x0c\xa8\xae\x1f\x08\x916&3\xb3E\xc5\
+\xcfI\xb3\xc8~\x09\xab\x91\xaf\xb6\xde\x1c\xe8?%\xba\
+\xf3\xe5\x90W'O\x96\xafV\x9f\x1c=\xaa\xbe}u\
+\xd5\x9d2*\xa8g\xd5\x0f\xdcx\xd1\x07\xec\xa8\xe5\xea\
+\x10\xe9\xf7\xdf\x86\xfdMU\x8aV\xb2\xad\xf2\x0b\x9b\xa3\
+\x98\x84,\xef\xd0d\xe7\x86X\xf4\xb2\xc0\x9f\xa0dL\
+\xf8\xf5T\x9d\xa3\xc0\x17h\xd78v\xb5[\x14M3\
+\xd3>J\xc7g\xf5\xd1\xc85\xda+\xfc\xd5\xcb\xee\xcf\
+=Yc\xcfN\xba\xcf:\x9f\x9d\x9d\xbc\x1fX\x1b\xd0\
+\x8b\xabYe\xbe=\xf5\xef\xf0\x16U\x80\xd0\x10\x9a\x00\
+R\xd2\xbbf\x03\xb2d\xad\xd8\x8cn\x08X\x183\xa7\
+\x0f\xf5}5\xc3\xb5\xac\xb0Z\x06\xd5\xeah\x904U\
+@\x91\x85a3\x16\xa4\xec\xb0\x85Q\xf3\x100\xee\x1b\
+\x15$\xea\xaf6BM\xf6\xed\xbb\xac\xb1i8\xae\xe8\
+zC\xaaQ\x861Q\xab9\xa4\x0c\x07H\xe3\x0e\xb3\
+\x06aX\xb9\xb31\x93\xb0\xf7Xy\x8eV\xd9\xe1\x18\
+>\xdc\xaa\xc9=\x8f\x89\x0dA\xc1\xeaBiINZ\
+E-\x00\x98P\x87qS\x0e\xc2\xa3\xacF\xe7h\xf5\
+\xa2\x88#\xe9\xd0\xf0h`C\xa3\xb7P\x1b\x84\xaf\xd1\
+(\x13\xd8\x17\xae-\x91\xc3K\xa7f\xae\xc6\x059\xa7\
++\x17\xf3\x14H\xfd\x8d\x026\x08TiO\x92\xb9J\
+-pr\x89Z\x9ef&k\xd4\x80b\xe8U\x0e\xc8\
+\x22\xa0\xf4Qu\x1d7\x1b\xe6\x80e \xb40\x19\xfa\
+HV\xecVC\xaa\xfc\x1c\x89I\xf6\xf2\x81o\x06\xb5\
+\x85B\xaf\x87m\xe6\x14\xd7\x0a\xdb\x08\xb1P\xea\xf5*\
+6\x8aZ\xd8\xa0\xff\x84\xd6\xdf\xb5Q\x93dK\xf6U\
+\x83\xb3Y\xac\x1bX\xb1}P\xb9\xa0/j\x16\xd0Q\
+\xa9\xa6F5\x8c:\x8c[x\x04a\xd1\xf5\x9e#\xe4\
+a\xf6\x0b\x01\xe0p\xee\x8d\xd2\x11\xa3&?\xd5\x99\xca\
+ Z\x9a\x91\xf3h\xdb{\xf6\x89X17\xe0\xf9%\
+'\xb1v-tv\xaeS.\x1a\xe4\xd0q\x07\x1b\x22\
+\x06\x92\xdc>4\xf9\x12\x1b\xdc\xfe\xe8XR\xde=\x0d\
+$\xf1/\x09 \xd3@QN\xe3 Y\xe5zH\xaa\
+\xac\xd6\x0e\xa8\xa8\xde\x17\xfa\xc8i\x89e\x19u\xa1\x16\
+\xc6\x22\x8e\xa9\xbdk\xba(vP\x92+A\xbf'\xe0\
+\xe5\x10\x84}\xf40\x16\xc0\xb2S\xb4f\x04\x99T\x01\
+D\x14\xdd\xec\x08u\x08\xe7\x5cq\xc4\x12$'\x98\x94\
+\x1d1y\xdd=\xa2\xda\x18\x01\xd5\xb7)ZS\x89\x08\
+\xdf\x1e\xdb\x1b\x9b\x19\x97en\xd8D\xcc\x06\xce\xc8\x83\
+S{\x04K.\x90\xa0!\x84D)\xc3!\x01{\x88\
+\xd2@\xfa\xd5\xad (\xf77\xab\x07\x944)\xc5\x0f\
+B\xa1\xd1\xc2\xa0\x0a`k@.\xa4\x1dV\x83s\xac\
+03\xf3>P9X\xd4jmDK\xd1\x09\x046\
+AC\x9b \x80\xc4\x04\xf5l(\xe1\xa8\x13T\xa2\x85\
+brN\xd1\x8au\x8cN\x13\x94\xbd\x05\x82\x88OP\
+\xa4F\xc9AYr\x15\xeahT\xee\x86\xe4lz\x94\
+\xda\x14\x1c4\x16X\xfe\x19\xa1Z\xf2kbT\x81\xdb\
+0-Hu\xd8107G+\x13\x09\xf14\xaah\
+ \x22fe_\x12F\x94\xfbL\xee\xb7\xa3\x1f*B\
+\x19\xf41\x82\xb0\x91\x04A\xc94\xa2BMTSK\
+&\x8b\x96\x92\x01\xb2\x85\x1e\x17J\xe9\x06\xba\x85\x9a7\
+wR\x89\xc2\x92\x8d\x93Gl\xa2\xa7\x11\x1d\xf5\x7f\xbc\
+\x8dZ\x03\xb0\xa0\x18\xc7)L\xc2\xc9|\xe4h\xc4\xb6\
+\xb9\x9fA\x11\xac\xb9\x92\xf4\x097\x1a\x91\xa0\xe0\x96\xe5\
+\x7f\xbfO+\xfb\xc2\x1e\x03]\xbe.\x0a\xfdz\xf6\x0d\
+46\xfa\x92C\xd7|\xedc\x81\x97\xd4>\xae\xa99\
+\xd4>\x12\xe6\xa5R\x06\x02\x95\xc2(8%\xaa\x0e1\
+5\xd2\x82\x5cWz'-\xa5{\xac2_%\xa0\x9e\
+\xb0V\xd7PpD@jT\xa63\x0f\x8f,L,\
+I\xfbL\xe7I\xa8yC\x99\xce\x14o0\xd3\xcd\x17\
+\xcd\xf3\xc9O\xfeL~\x7f&\xbf\x9bI~!\xcd,\
+\xc5\xae7#\x16\xda\x81\x98\x7f\xe6\xc9\x7f\x93<\xa9\x90\
+c\xac\xdb\xed\xb4\xd0\x9d]2\x1c3\x89\xe3\x87\x99\xc4\
+\xe1\xc3Lb\xb9\x93I\x1cv3\x89\xebn&q\xfd\
+0\x93\xb8~\x98I\x5cv3\x89\xcb\x07\x99\xc4\xe9\xc3\
+L\xe28f\x92\xd9\x18\xcf\x17\xd6\x07\xf6\xbb\xaf\x0f.\
+\xcf\x93w\xc6D\x89\xd8\xdc\x16H\xd1\x22\x5c\xf8*\x8b\
+\xc18Lc\xd7\xbdO9\xda1I\x93\x95\xb7\x80s\
+y\x8b7\xab\xcd7\x17a\xce\x0d\x00\xd1H5\xb0\x03\
+\xa6\xb7\xecq%b\xe2\x02/|'\xe7\xf2\xba\xe2\x22\
+\x93s\xf8}\x9a\x9c\x13\xed\x94\xa4\x88\xadwx\xcd\xf5\
+6\xca\xf6\xe6Z\xacv\xc9pks-\xc6\xbd\xb5z\
+-\xfde\xd0?p\xdca\x1bo\xdd\xdaa\xcb\xed\x0d\
+6\xe4\x06\x8c\xd4!\x9b]\x1b\xd4\x86j\x05mv\x93\
+\xa6\xfc\xed\x0b\x9c\x0e\xfa)^\x94\xcdBh\xde\x8b.\
+0\x02\xfaR\xbch\xd7i\xe6\xdcj\xf4\xa2\x01\xf0h\
+\xae\x98\xa1k@\xb22\x14\x1a\xedz\xe2\xfe\xa5\x1d\xc1\
+\x9c\xd6\x0e\xde\xc0\xde\x1f\xa8\x5cn\xefh\xad\x01\x8a\xb3\
+\xfa\x15\xb57\xef\x1a\x9c6u\x8da\xdb\xb0\xf6:\xa9\
+\xdf\xe5\x9c\xee\xce\xc2\xf0Wb\xd5a{\x15\x94\xf2\xd1\
+\xa6\x09-\x093E\x17w\xb21\xd8P\x05\xb6L\x12\
+\xb2\xbc=_\x1bD\xea'\xf2}Csg\x9fa\xee\
+>]\xd1\x87gS\x8f/9\x0e(c\x09h\xdc\x12\
+\xd9\xb8\xf6\xc1\x19\x1b\x93*\xf5\x8b9w\x12\xa7#\x8e\
+\xa6\x12\xea\xd1\x87pN\x009\x12k\xa0\x89\xe6\x1b\x8c\
+\xab\x9e\xc6\xa6NA\xb8\x85\xba\xb6d \xcc\x1a\x87\x0c\
+3t`F\xb3%\x84\x13_-\xc2D\xfa\xe1\xf3\x11\
+\xf0\xc5\xceGXs\xac\x9cG\xd0\xd8\x83p\x1b\x93h\
+!\xe2\x93\xeb4\x1b\x1bR\xe4P\x8a\x130\xc1\xb0.\
+\xb7\x00\x8f\x9a\x0c0T\xebS\xa6\x81\x22\xf3\x91hs\
+\xa3D\x1f\xb1\x9a\xf4h\xc6(l[(W\x0eL\xd7\
+\x1e\xc3 \xef\x98$m\x14\xe1Z3\xc9\xa9\x80\xf2\x07\
+\xb0\xa3D\xb8\x82\x1d\xd1\xef\xcc\xaf)\x1a\x12e`_\
+?\x99\x1by\xcdQ\x88\x93da\x96AaG\xac\xcd\
+\xd4\xb5\x90$\x02\x8b\x9aKjh)\xe2\x85:\x889\
+\x17=W\xa0,\x885\x89\xcb8\xa4\x0f\xa5\xfd\xb8B\
+\x22\xb1Z\xe1z\x14Je\x8e\xe9\xa0\x13T\xb29$\
+jNP\x83\x06J\x1e}\x01IM\x18\xb2\x0c\x11\x1b\
+9\x93\xe2\x91{\x0b\xc2@*sgB3\xe9\x17\x90\
+\x988\xc8\x8d\xac\x0e\xabO\x88\xa0B\x9a\xd4\x95\x14\x0a\
+)G\xc1\x0dB\x98\xa2\xa7\x08FAW\xb0\xa4\xa3\x1f\
+\xf6+\xcb\x9b\x00)p\x99.\x90\xa8\xea\x11gC'\
+\xd5,\x0c\x01\x1d\xfdH\xa0\xa5:\x04\xf7\x98\x92Z\xef\
+6\xd9\xaa\x97\xa2PH\x87\xa4\x81\x22*Y\x8f1\xb8\
+\xe9\x9e\x91\xf7Zvn>\xcbQ\xff\xea\x87`\xbe\xf9\
+\x7f\xaf\xd7Z\xe4TM\x01\x00\
+\x00\x00\x17\x91\
+\x1f\
+\x8b\x08\x00\x00\x00\x00\x00\x00\x00\xed=io\xe3F\x96\
+\xdf\xfbWp\x9d/i\xacH\xd5}\xb8\x8f\x01f\x82\
+\x04\x016X`&\xc1\xcc\xb7\x80\x96([\x1bY2\
+$\xb9-\xf7\xaf\xdf\xf7\x8aW\x91,Q\x94,\xbb\x03\
+\xc7v:\xa6\xaa^]\xaf\xde\xcd\xaa\xa7\x8f\x7f\xdb\xdd\
+.\xa2/\xd9z3_-?]\xd0\x84\x5cD\xd9r\
+\xb2\x9a\xce\x97\xd7\x9f.~\xfb\xf5\xc7\xd8\x5cD\x9bm\
+\xba\x9c\xa6\x8b\xd52\xfbt\xb1\x5c]\xfc\xed\xf3\xbb\x8f\
+\xff\x15\xc7\xd1?\xd6Y\xba\xcd\xa6\xd1\xc3|{\x13\xfd\
+\xbc\xfcc3I\xef\xb2\xe8\xfb\x9b\xed\xf6\xeer<~\
+xxH\xe6Ea\xb2Z_\x8f\xdfGq\xfc\xf9\xdd\
+\xbb\x8f\x9b/\xd7\xef\xa2(\x82q\x97\x9b\xcb\xe9\xe4\xd3\
+E\xd1\xe0\xee~\xbdp\x80\xd3\xc98[d\xb7\xd9r\
+\xbb\x19\xd3\x84\x8e/j\xf0I\x0d>\xc1\xd1\xe7_\xb2\
+\xc9\xea\xf6v\xb5\xdc\xb8\x96\xcb\xcdw\x1e\xf0z:\xab\
+\xa0q6\x0f\xdc\x01Qk\xed\x98\xb01c1@\xc4\
+\x9b\xc7\xe56\xdd\xc5\xcd\xa60\xc7PSF\x08\x19C\
+]\x0d9\x0c\xear\xb7\x00T\xec\x9d\x8c\xab\xf5G\x07\
+\xf4\xdf\xc1\xbf\xaaAY\x90lV\xf7\xebI6\x83\x96\
+Y\xb2\xcc\xb6\xe3\x1f~\xfd\xa1\xaa\x8cI2\xddN\xbd\
+nJ\xec7\xc6ml\xc92\xbd\xcd6w\xe9$\xdb\
+\x8c\xcbr\xd7\xfea>\xdd\xde\x0050\xe3>\xded\
+\xf3\xeb\x9bm\xfdy>\xfdt\x01\xeb\xe3\x82\xe4\x9f\xcb\
+\x19\x5cVtD\x12\xcer\xd0\xa2[\xbfJ\x98\x84F\
+k\xab\x15q \x0d\xe2kt7]Mp\x86\x9f.\
+\xbe\xaeV\xb7\xf1\xea~\x9b\xc0\xa8_\x9b\xfdB\xe9\xdd\
+\xfd\xf6\xf7l\xb7\xcd\x96y7\xb00o\x95\xae\xda\xb5\
+K\x1aK\xac:\xc8vw\xab\xf56\x9e\xcd\x17Y>\
+\xd8\xf8fu\x9b\x8d\xef\xe6KX\xf6z\x05\x0f\x93\xcd\
+x\xb5{\xbc\xce\x96\xf1|\x02\x946\x86v\x8b\xf4j\
+\x91\x8d\xd3\xc9v\xee\x0an\xd3\xc5b,\xccN\x98q\
+5\xd5\xbb\xe5up\xa0\xdd\xf4\x0e6\x96r\x19\xac}\
+\xacj?C\xf5\xc7i6\xdb X\x8es\xfc\xc4\x05\
+%\xae\x0ej\x81l\xb2t\xfd\xd3:\x9d\xce\x81Yr\
+8\xaf\xcb\xc9j\xb1\xc8&\xb0o\xe9\xe2!}\xdc\x5c\
+T\x00\xd0U\xb3)\xb7T\x15\x9dB\xb7\x9b\xed\xea\xae\
+\x84\x85\xdd\xd8>.\x00+X\x18C\x8f\xab\xf5\xe5w\
+\xf4\x8aI\xce?\xb8\xa2\x15\x90\xcf|\xfbxI?\x5c\
+\xd4mV\xb3\xd9&\x83\x81\x89W\xe6\x88\x06Z\xc0X\
+ R\xc6O\x1b\x8d\x84F\xa3\xc1\xd1\x18\xa9F\xfb8\
+n.\xfbiht\x5c{y\xb3\xce@\xca|\xf7\x9f\
+_\xfe\xe7\xe7\x1f~\xb7\xbf\xc7\xaa\x07\xcd\xccP#\xaa\
+\xfa\xeb\xa2\xf4\xb7\xe5|\x0br\xe4~\x93\xad\xff\x85\xbc\
+\xf8\xbf\xcb\xdf6Y\x07\xea\xd7u\xba\xdc\x00\xe3\xdf~\
+\xba\xd8\xe2\xe3\x02D\xef\xf7\xcc&Vq%\xed(\x06\
+\x92I\x94b\xf2}=?\x0a(\x11\x168\xd1\xaa\x1a\
+5\x8fPjd\xc2\xb9\x10\xa4\x9e\xcb\x8e\x01\xac\x14\xd8\
+\x83\xf1`\x99\x0f[aq\x0f\xbe`\xb9\x01,\x0c[\
+%\xce\xd5\x8a\x84\x0bn\x08o\xcc\x95\x12\x06\xc5B7\
+\xa6jT\x22\xb9\x94\xd66\xa6\x0a\x1dHD\x86\xd9G\
+\xca!\xb2\x0c\x10\x9c\x9c\xe0o\x0fy\x97\xc4\xa5X,\
+\xf7\x93r\x09\xa5\x85f\xf1\x81!\xb3\xd9\x8cd\xb3!\
+\x1c\x95p\xca\xadPz\xd8\xc0$\xa6\xfd\x03\xa7iz\
+\x95\xdaA\x03k\x06RB)\xbb\x7f\xe0\x10#\x86\xf0\
+\x0b['\xe9\x10\xfc\x0a\x0f\xbf\x7f\x06\xe6=\x96\xac\x03\
+\xcc{\x9bn\xd7\xf3\xdd\xf74\xb1\xf8c\xe8\x88\xc0\xaf\
+\xca?Y\x09|\xccXB8cz\x14+N\x81!\
+-\xb3/\xc0\xd38\xd1t\xf1,hlv\x8dh4\
+gC#\xe0\xca\xfd\x18\x87\xc6\xe2\x13\x05,\x0a\x05\xeb\
+\xe6\x14\x1f\xa9\x86\xd5r]#q\xb2C$\x22\x00\xf3\
+$\xc8\xe4\x11\x0d\x1c\x82]\xd0\xbat\x16\x84\x9d\x05a\
+\xd7\x9f.\x10\xb1`\xf9\x1c\x12\x95G\xa1\xb5\xd9\x87\x16\
+\x96\xf5S\xa9%OG\xef\x1e\x15c\x90\xf0(o\xd3\
+\xa3N(Umj\xd4Fs\xd3\xa6F\x93hi\x0c\
+oP#\x85!\x18\xb7\x034\xcc\xf1\x86\x8d\xc3\xd6p\
+Sc\xe6~N4l`,q\x94a\x13\x1am\xb0\
+a\x03\xa3\xa9C\xb2\xf1\x1cL\xdd\xc2'\xa5\xa6\x9f\xb9\
+=\xea\x1cF}\xc8\x8dF'\xb2\xc1\x88`\xcc\x18-\
+\x89'\xcd\x90\x11\x8dM\x8c\xa0\x86\xcb\x06#\x828\x94\
+Z[\xdb`D\x0a\xa0\x9a1\xd2\x9dMW\x88\x10\x90\
+\xbeT\x12.\xc5\x08|(\xca\x09\x98\x1b\x84\x8f\x8aG\
+\x94-\x08 \xa4\x02i\xc2\x13\xcb\x15e\x9c\x8f(\x05\
+\x8dH@\xb6\xbe\x1fb\x19\x05\xd08\x9cP\xa6|6\
+\x19D(A\xb2\xa4\x9e\x05\xbc\xdfV(}\xbbV\x87\
+\x89\x94 E\x89\xc7\xc8\xc1\x19^\xa539k\x99L\
+$a\x80G`w\xf6a\xd0\xf8*8\xbe\x96R1\
+\xea\xedcp|\xc3\xafdv\xd5\x1e_X\xb0&-\
+\xeb\x1b?h\x9e\xe8\xd9D\xb5\xfb\xd2F\x08\xc3\xb4\x19\
+\xcc\x9f\xa0\xc8\xbf\x01\x7f\xc2\x0c{\xf8S3o#\x9d\
+\x1a\xd4 \xbbA\xb3\xd1\x06\xf7\x09\x96(\xa9%k0\
+_\x17t\x16\x02\x05\xde\xe3,\x91\x94i\xd5uuB\
+v\x90\x06\xfa\xe29\xbbY\x0e\x8c!\xb4\xe3B\x0b\x9e\
+\x00\xb0\xdc\xc8\x01X\xc58\x9aF\x14\x14\x856\xf0\xa8\
+$\x10&h\xf6\xf7\x03\x85\xcd3h\x16\xc4\xf4p\xa2\
+\xca\xcd\x94S5\x0b3=,<p\xb4\xe1\x9a\x85\x99\
+\x83\x94;\x9b/\xb6\xd9\xba\xa2\x19\x1c5\x9e/\xa1\xe8\
+n\x05V\xc3|\xb5\x8cs\x08\xd8\x89\xcd?\x7f\xfa\xfb\
+\xc5Q\x88\xce\x9b\xc24\xbc\x98\xc4,\xfb)\xbd\xdfl\
+\xe6\xe9\xf2\xef\x8b\xfb\xb57\xe9C\x1d\x22~\xa6?d\
+_\xe6nZ(Q\x94\xd2(\x7fuk\xe9\xcd\x11`\
+t\xe3!!\x9f\xd2\xb3\xb1-\xf8\xbd*\xe6\x9e\x93\x18\
+`]\x11\xd0e'{\x1f\xce\x5c\xa6h\xbf\x0aC\x14\
+<\x03\xcb\x81\x024\xc0\x5c\xa2e([0\x00\xad\xa6\
+MC\xd9\xaa\xc4(#\xa9l\x8a\x88\x0e\xec,\x08\xdb\
+\x90\x11\xe7g\xcd\x0a\x9b/d\xf8a\xd4\x22\x96q\x8f\
+c|n\xe3\x0f\x9d7\x18\xf1`d\xeb%\xd8T\xd0\
+\xe7`Sp\xe5\x8c\xe5\x82\x9a~.\x15t/\x97\x9e\
+/0@\xc9\xef\xb1\xe9\xa17\xb4z\x8f\x0d\xeb9\xc7\
+\x09\xb4\x98\x12\xc6c\x0ct\x9d8O81\xcc3r\
+\x9d\xebdMB\x0d#\x1e,s\x06\xb1\x92F\x0d\x0f\
+\xcd5\x97rDl\xae9G\xea\x05\xe9\xcax\x9cQ\
+\xd2\x93Pe<./}Z<\xeeG\xf7\x13\x8a\x0f\
+\xd9X==\x1a%\xc1~\xe1\x81\xde5=\x1c}\xfa\
+6\xaa\x80\x1d\xedd\x9dG\x15<\xaf\xb1\xf8\x9cA\x13\
+\x22\xa8\x17\x09\x0d\xb3\xf0\xb1\xc1\xbd\xcd\xdd:K\xa7\xbf\
+d\xdb\x9b\x15nQ6\xc3Y5\x19\xdc\xf0D\x11\x22\
+\x9b\xb1\x11@\x0d\xe1\xe0D6\x19\x1cX;\xe1hy\
+\xf9\x1c\x04\xd60\x9a+'8\x98\xf9\x82_*\xf2A\
+\x04\x8f{b\x1f\x1e\x9c\x8de\xa8\xd3\x84\xe6\x01P\xd5\
+\xcf\xab{\x94\xe6\xcb\x05]\xc0%\x01\xb4\xfe\x09\xb4.\
+\xd3\xe2Y\x8ccj\x19\x15D\xb1\xd6\xe2\xdb\xc6\xb1V\
+\x7f.\xe3\x98\xdb\xf3\xc5\x94\xff\x94\xc6\xf1\x8bXr\xec\
+\x99hJ\x0a\x05\xfe\x94\xed\xa7)\xc1\xf6\xd2\xd4\xf9\x15\
+B\x90\xa6\x9a J\x883\xbc\xab\xad\xc2\x1c\x84\x81&\
+\xe0\x18K\x04\x85\x00\xea\xd1(GVDr\xc2\xb4A\
+r\x93DK%G\xb1P\x09W`\xd8b\xc4\x9da\
+\xc0\x1dTf+\xe2Nh\x229\x15\x0d\xb5BM\xa2\
+@\x89h\xdaP+]P(\x94\x00J\xa1[v\xde\
+\xf7?-\x15\xc4)\xebeY\xcf\xb6G^\xd2\xf8J\
+\x0c\xd4p\x93\x97\x04\xe2\x881B\x1b\xbc\xd4\x85\x9d\x05\
+a\xeb72\xc36\xf2\x19\xc2D\x88\x85\x97R\xc3\xb8\
+\xcf/\xa7\x0b9\xfd\x16/ \x0eQ\x15;\xe5\xfd\xc3\
+\xf3\xd2\xde\xd1\xd6\x1b7V\xc7:\xe6\xfb\x08\xa7\x0a\x03\
+\x18kc\xd1x\xc5?\xd0\xa5\x9aM\xf0\xb7Eg\xc7\
+\xd1\x8e\xc1\xdfv\x0f!\xd3N2\x06\x0e\x96n{\xf2\
+\xf9\xe9\x1c\x02NV\xac\x0f\x9b\x8e\x00\xc9c\xb6\xc7x\
+\xd4\x8cs\xad\xa5=\xb0f\x92\xcd\x02g,\x86\x0c-\
+]x'4\xb4\xb1\x84\x09\xdf\xcf\x09\x1f\xef\xe0\x19\xcd\
+\xba'\x1e\x86\x0c\xada\x83CoF\x0e\x8c8\xc9&\
+W\x93\xab}\x1b|\xdcY\x8a\xae\xb7\xc4\x81\xfa\x0fL\
+\x9d\x09r\x02]\x12\xab\xcc){\x04\xa3\xf1\xe3\x91\x94\
+f\x93Y\x87\x0b>\x9c\x0bKBR\x036\x86>\x84\
+)!\x19\x89i\x98\xb4\x0f\x90U\x96\xc1>?\x89\x8b\
+\x05\xfe\x0e\xe1b\x91\xbfS\x08i\x02i\x15\x8a!~\
+x\x9b`\xa9,\xb6q\xe8M\xe3\x81\xad\x9a\xa9\x99\x9c\
+\xc9\xf3\xd0\xf3S\xecFK\xfb\xce\x08\xe1\xcb\xc6\x86\xa5\
+\xa68*\x0b?t\xefB|x\x22E*\xaf\x14M\
+\xb5\x00,\xcbc&j\xf0\x89\x99g\x8c\xa1\x1c\x5c\xba\
+\x17\xb1\x1c\x1eX|>\x04\xeds\xec\xf0\xb5\xb0;z\
+%\xd0\xcb3\x18_1D\x1f~\x89\xff\xac\xa8\xb3G\
+\xa2\xae\xef0\x03\xe3D+ \xc4\xf6jc)\x13j\
+(\xd4\x8e\x18b\x12\x96m\xde?\xd3f|Kd\x0a\
+\xfe\x5c\xc8\xa4\xc5i\xb3\x06.1l\x07\x980\xf4U\
+\xe2R\xbf(.\xe3\x22\xe0I^%2%}v.\
+\xe7\x0dlZ\x89\xe7J\xa9}}\xc8\x14D\x9e\xf2\xc6\
+\xed\x95,\xfd\x8c\xc7\x90{U\xe2+D\xdd\xf9\xc2\xc4\
+\x7fuE+\x88:\xc3A\xe37E[\xe0\xf2|o\
+t\xff\xea\x8a\x16\x90y\xbe\xc0\xfd\x9b\xa2Uo\xda\xe6\
+d\xd4\xbdi\x9b\xf3!S\xbfi\x9b\xf3\xe1\xf2M\xdb\
+\x9c\x11\x99o\xda\xe6\x8c\xc8|\xb6\x80\x83\x87L\xa1\x12\
+*\x99\xb2|\xe4.B\xbeN\x1e7/ /kD\
+\xc6\x94\xe3\xcb\xce\xd7\xc9\xe1\xe6\x05\x22\x8a\x1e*\xad\x82\
+RA({\x95\xb8<\x9f9\xb9\xdf&\xf2\x90\xa9\x11\
+?\x8a\xda\xd7\x89\xcc\xf3\xbd7\xe8\xd8\xe61\x1e\x1d\x02\
+\xe98\x8a\x0d\xae\x14P\xc8_#\x0a\xcfx\x94\x9a$\
+\xdc\x08E%7m\x5c\xeaDZ\xa6\xa5\x10#\x87\x1e\
+\xf2:5\xce\x19\xcf`\x0eAeI\x97\xe65\x1aB\
+\x94\x9c\xd3\xaa\xdc\x87Lf\x12\xcb$\xfc\xbcf\xba\xa4\
+\xe4\x9c*\xe70*_9]>\xff\xab\xea\xbf\x8c\xb7\
+C\xe99\x95\xcf_\xdc\x0f\xa7\xf4\xd8\xf3'o\x01\xa2\
+\xfd\xb8|\x8b\x5c\x9e\x0f\x99\xec|\xef\xfdi\xd2\xc5\xa2\
+I$\xe7\x94Z\xf3\xaa\x83\xe9\x94\xfde_\xf8S~\
+,\x01\xbd\xd8\xc9\xba:O\x14\xe8\x16w\xbfiD\xbe\
+\xe5Q:\xc0\xd5\xf9b5!f\xa3\x0c\xd9\x8dP\xf6\
+R\xcc\xf6q\x8cY?\xddS\x95\x17\x15\xf3\x94N\xbf\
+\xcc\xb3\x87w\x15\x1a\xae\xd2jIw\xe9u\xe6N\xcd\
+\x02\xf2\xf2\x1b\x1eE\xc5\xd5j=\xcd\xd6e\x95r?\
+\x8d\xaa\xe2`m\x9d\x89\xd5\xdb+\xec\xb5\xaa'\xe1\xfa\
+\xcdM:]=|\xba`\xedJL\x8a\x0a\x0bk\x17\
+\xe3\xd5\x0b\xa1\x13\x0b\x9ec\xa7\x09^\xc0\xd0\x98F\x16\
+\xdc=\xda\xae\x9c\xae&\xf7\x98\x1f8\xbe\xcf\xb7\xf6n\
+\xd7i~\xbf^#\xc0\x22}\xcc\xd6\xcdl\xb5u>\
+[c\xaa\x86E\xc6[\xafds\xb3z\xc8\xd1\x82d\
+~\x9f\xb5G\xc0z\x7f\xd1\x1e\x0cV]\xafq[B\
+\x0d\x1f\xe6Kh\x10\x979v\xa9\xec\xac\xbd\x80(\xa7\
+\xa9M\x07s\x05\x04\xe0O\x8a}\xcd\x1f\xf1^\xbe?\
+\xa3\xfb\xf94\xdb\x84\xe7\xe4\xea\xe2\xab\xab\xd5.\x5c\xbf\
+\xba\xfa?`\xd4\xf8.\xdd\xde@\x0f\xb3t\xb1\xd9\x07\
+\xb2\x5c\xb9A|\x90\xbcf\xbbZd\xc0b\x93\xac>\
+\xeb\x8d8\xf2\x8bM\x83\x1a\x8b\xad\x0b\xe2~\x99\xde\xc5\
+\xd7\x8b\xd5U\xba\xe8\xc5\xf1m\xba\x9b\xdf\xce\xbff\xd3\
+\xfa(}\xb3\x0fo\xc5\x85\xc8\xaaQ\x02\x93+\xd9r\
+\xfb\x88Y\x94w\x8fX\xd6\x90>X\xc0\x84\xa8-O\
+\xcc\xa6<_^\xef0\x83\x05\xf2WENU\xd5c\
+\xa0*\xbb\xbd+jk\xe6\x89\xa2/\xf3\xcd\xfc\x0a\x0f\
+\xc2{+\x04\xd8%\xe6\x1e\x9e\xb6Jq5\x05<\xce\
+\x09\xa5\xe2f\xb5\x5c<\x16`\xa5,\xe9\x8a\x10W~\
+\x9bm\xd3i\xbaMkyR\x96p\x90\xa8%j\xd6\
+\xd3\xd9\xe5?\x7f\xf8\xb1:\xe3?\x99\x5c\xfe{\xb5\xfe\
+\xa3\x9cB\x14!@z\xb5\xba\x07\x9a\xad.=`\x12\
+\xe3\xc9%\xca\xd5t\xfby~\x0b\x0c\x83\xa9\xb1\xff{\
+w\xbb\x00\xc9VU4\x80\x11\xdbu\xa7y\xb7\xeb,\
+O}\x1d\xcc\x16>\x9d\xdc\xce\xb1\xd1\xf8_\xdb\xf9b\
+\xf13\x0e\xe2\xddE(:\x9do\x17\xd9g7f\xfe\
+X\xaeb\x5c,\xa3\xbcI\xe0\xad\xf2\xe3\xb8D\x83\xfb\
+t]\xa3\xe7Z\xab\xeav\xe86\xa4\x0eM\xa2\xf16\
+'S\xa3\x18\xf3yf\xb1z_\xa2\xf1\xba\xa2\xaaP\
+xA\x08\xa6\x18z\x1a\xa0}\x0cxj\x98\x01\x8bY\
+\xc9a\xc0\x91\xab\x96V\x13\xcc\x13Jt\x02\xda\x87\x83\
+\xed,y\x22\x89\xf0#\xdc\xf9\x14%\xf1R\xe0\xac\x81\
+\x05k\xa4\x167,\xea\xf4fVSB\x85d\x1ff\
+\x80\xc1K\xc0\xed\xf7-\xc5\xebr\x17\xbfw\xd5\xde]\
+\x99\xcdv\xbd\xfa#\xbb\x5c\xae\x965)\x16\xf7\xf2`\
+<-\x94\x7f\x83\xa5\x90x\xad\x1b\x99\xb58\xc6$\x22\
+.\xd7\x97W\xe7rIX\xc0\x00o\xe4\x87\x03\xba\x8e\
+\xa5H\x80\xf5t\xa3\xab\x10J5\x81\xb5\x81\xa6\x1ey\
+\x8fq\xb8\x98\xf8\xd6K\x07i8(\xa8+\x85\xf1\x9f\
+\xf6\x14U\xa2\x98R*\xb0*\xeeR\xbd\xb2\xc6\xfd\xa4\
+R\xf6c2*\xc5;7\xa3s\xdc\xe9@\xce\xbd\xbe\
+\xddQ\x03w\xe7\xcc8B\x9d\xe0\xcd\xb3\x140\xb9\xc8\
+L\xd7\x93\xbeE\xe0\xec[\xa9a\x8f 1\x1cY\x0b\
+\xd3\xb8$U\x0e\xefn\xe7KD}\x83\x9c\xeaz\x97\
+\xc1\xd5\x09b\x13\xaa_\xef\x02\x84Z\xd7>\x06jQ\
+jFTj\x8c\x0b\x88Q\xd9y4\x8901\xad\x15\
+R\xc8\x08d\x017 \x18Fy\xe3(\xce\xffV\x1f\
+s8`\xf4\xaa\xa6lQ\x17\x14\xb0\xb0\x0d\xd5\xbfE\
+TT\x92\xe8kh\xba\x9bm\xban\xdf&+\xeb\xb2\
+%\xcc\x9b'TP\x8c\xab\x9f\x8fL\xd0\x1d\xa0\x84\x80\
+C\x10\x03sh0\xe3\xa4\xed\xe1\xad\x93\x04\x92%G\
+\x0b$\x1b\x10Hn\xa3\xa4\xd9\xcb\xbb\xfe]\xdf^\x89\
+\x14\x10\x0e\xe7d\xb6\x8f\xe3\xeb\xe2\xc1\xe7\xb9\xee\x084\
+\x91\xda\x12\xa6\xc0\x17\xc3\xc4\xba\x8c\x0b\xc5p\x80\xea\xb9\
+\x06\x88a%\x12s\x01\x80\xc2\xe1\x12<\x1b\xc6\xbc\x10\
+LN\xd1\xa0h\xb8flT\xe5du$Mq\x8b\
+$\xa5\x22\x8aMb@'\x19\xd0Te\xd2\xd5(.\
+\x1e\x1bEe\x0b$\xef\xaa|T67\x8d\xc2\xba\x1d\
+\x19\xd5-\xa3\x12\xd6\x07\x08\xb6\xf2\x07+\xe7R\x0d\xd4\
+\x9d\x5c\xcd6\x0d\x1e\xeff\x91m\x08\x88\x9e\xeap\x12\
+\xdb\x86xj$\xbd\xad\xe4\x99\x9fI\xf9\xb0\xbc\x04\xa5\
+?\x84\x03\x02R\xb9\x22\xaa\x1es\x04\xdc@%-\x11\
+.9\xa7\xa2`b(G\xa9\xd5s\x0d\x10S\x8e\xc9\
+9\x15\x03\xd3D\xa9\xc4p\xa2E\xc7 !\x0a\xd3\xff\
+\x0c\xd3\x1c\xb5_\xe1.\x88\xfa\xcc\x5c\xd6 \x1a\xee\xc1\
+\xd7\x88\xf1\x8e\xb9\x00S\x88\x89\x10\xd8j=\xbf\x9e/\
+\xd1S\xf8%\xa2\x86\xe2\x97\x0c\x80<u\xb9\xa8`\xba\
+\xd1?\x22\x0a~\xb8\x02\x863u\xa1\xcb\xb1\xee\xe0\xa4\
+J\xac\xe1\xc0/^\x99\x16u\xdb\xaa\xd0b\xae\x0e\x03\
+\xce\xaa\xd7\x1f0q\xd9a5p]\x06\xad\xadM\xc0\
+\xc4\x13\xcc+ex\x92\x862\xbf\xc3\xaa\xc8\x1b\xb8*\
+\xf3&X\xf5V\xaf\xa3\xbb\xde\xafQ\x03\x0d\xb2x\xc0\
+\xc9\x00A\xc2s]\xc6\xd0@q\x8d\xa4I\x84T\x1c\
+8\xa5.kL\xa6,\xb4$1(PL\xdd\x1d\x05\
+\x89\xe1/_8\x94KP9\xdc\xe4\x95\x92\xf9\xed\xe0\
+\x93\x8f`\xf8X\x8f^\xb5\xab\xe6\x18X\xca\xd7h\xb8\
+\xcd\x81\x19q\xfbX\xe8C\x9e\x88&oZ'&}\
+\x1f2E0+R\xdb\x1c(g7\x12\xa0cA\x1a\
+\x80\xec\x8c1!\xbd\xb1\xb9\x9a\x07\xae\x11\x0a\xcaGT\
+$D[N\x94WV>\xa0\xb8-\xda\x00\xb6r@\
+Z\xd7v\xe1\xbd\x11\xb0\x0c\xc7\x8f];\xa3@\xfcU\
+E^\xffnR\x96\x10\x85\x82\x1a ]\xe6\xa3\x1a \
+\xee\xb6\xf9\x1a\xdd\xe2\xbc\x12\xdc\xa6\x09lL\x82\xe9\x91\
+ARD\x0c\xb3t#,R\xa8\xa2\x04i\xba,*\
+\x1f\xb0e\xde\x02j\xdd\xd91Ex^\x8d\xa35\xe0\
+\xb1\xc0\x01s\x87\xb1\xbc(?pf\xa9\x22\xb4.*\
+\xa0q5\x0e\x9e[`\x0a\x04s_\xa7P\x8d\xed\x83\
+\xe7\x04s@a\xc30T\xa3\xd7\x95G\x03\xab\x8fT\
+b\xde2\xc9\xe8H%L\x1a\xe2\xbe\xaaa\xafi\x1c\
+\xea\xd9PM\x99\xa6\xb6\xe8\xb9\xfa\xc8\x81Z`i\xb0\
+\x070\x7fp#\xad\xd5\xef{h\x0b\x5cL\xcb+\xe2\
+\xb2`\xe6hG\x5c,A.B\xda\x92\xb0b.\xea\
+\xa2\xe2oNY\xd8@\xa9\xa8\x04\xab*\xdb\xc0^\xdf\
+X\x84\xe2\xc7\xd1\x954\xa0\x89\xb8WV\xf7^\xcc\x07\
+\xd4\x81\xa3+L\xe6m\xea\xfa\xb8\xdb$'+\x0e\xa8\
+\xc8\xe9\x0at\xa85\xd4\xd1\x15~y\x00\xd8\xd5 (\
+\xc0\xd91\xc5g\xf7\x7f\x5cF\x01\x88\xf4\x8b `p\
+\xba:\xd7O\x5c\xc3\xe6\x9f\x1d\xb4\x16\xd4\x11TQ\xea\
+\xd2-\x19\xc3\x0d\xc8,\xaf\xb0j\x84\x8b)\x9a\xf1(\
+\x07\xa5,\xaf\x97Q\x13\xbeIR\xb5\xafB\x02\x09\x1d\
+j\x93W\xe2\x11\x1a\xe0\x98\x0f{\x85\x95 \xbd\xfa>\
+$\xac\x04m\x0a\xab7\xad\xf8\xa6\x15\x036\x14&\xfa\
+\x07\xa5bI\xd0\x1b\x0e\x9bd{%\xdd!\xf1\xa4Y\
+G:I\xbeW6i\x16\x15 \xfd\x82\xc9\xf5\xda\x92\
+K\xd2*\xd1#\x97\x94\xe4\xaa\x90K\x82\xc8\xa3\xe5\x12\
+\x88\x98\x86XB\xfe\x0d\x89%0\x91K\xb1\x04*\xef\
+\x90X\x02\xe9\x16\x10K\x8ah\xd6+\x96$\xd5&\x17\
+K\x92\xcb\xa3\xc4\x12k\xf8\xbe\x87#Mvp\x1c\xf0\
+M\xd8\xbc\x09\x9b\xb0\xb0Q\x5c`\x16\xc43\x08\x9b\xa0\
+\xdb\xca)\xd8g\xbc<\xbcS|\x8ae\x82\x01 \xab\
+\xac\x0b\xab)M4\x97}f\x15lo%\xb5\x18\xb8\
+\xb4\xb4\xb0\xd8\x99c)\xea\xde\xde2\xc1\xbd\xb2\xf2!\
+\x97]\xd8\x06\x0c\x9f\x12\xb0\xae\xed\xc2{#`Yn\
+\xdf\x82\xd5\xc7\xd1\xae\xca\x0b\xbc\xbe\x8b\x09\x19\xa9\x9d\xf1\
+\x0c\xc6\x9b\xb5\x1e@\xdcnQ\xda\xea\xa5\xa9.\x0dJ\
+.\x8eiM\x8b\x95\x08\xc1\x98TuQ\xf9\x90\x1b\xea\
+`\xe0Y+\xdcX\x02_\xb1\xbb\xea\x5cP\xfa\x0d\x5c\
+I\xde\xbd\xcdE|^\xe8\x16#@\xce\x00\x87\xd4e\
+e\x8b\xdc\x5cwmh\xe4\x00yQ\xeb&\xd0\x80w\
+%a!f\xfbm+j\xa1\xa9\x11\xfbm+60\
+\x96\xf2&\xd6\xde\xc4ZX\xaca\x0c\x97\x11\xdb\xf8\x92\
+\x9ds\x1aQV\xd5\x01\x04\xf0\xc2\x88.\xc4Q\x19?\
+\x00\xebM0U\x17\x15\x7fsY\xe4\x1a\xb8\xe8\x01u\
+\x01\xbb\xb2\xb2\x0d\xec\xf5\x8dER\x15\xb6\x14\xb4\x82f\
+^Y\xdd{1\x1f!s\x1f\x8fJKM]\x1fw\
+\x9b\xe4\xe2\x88\xe5\xec\xef\x04\x12\xac\x90\xe9<v\xa0r\
+\x81\xc4\xa0GpX\x8b\x82\xfcO.\x8c\x10V)'\
+\x8c\x00\x88SW\x99\x9bm\x1e\xb4,\x85\x11\x80+[\
+\x04\x0e\xca\xe5@KC$vR\x17V\x8dry\x84\
+\xcd\x8c\x88rPkFE\xdfQ\xb3\xc1^i$\x8f\
+{yg{_\xde\x05\x83PZ\xbc\xf9uo2i\
+\x80L\xe2L\x18\xab\x1a\x99\x1bO\x95IO\x88\xd1\x83\
+\x19\x06\xfcl\x1a\xaf\xc8\xdf\x88\xf6\x8dhC\xe2\xb1e\
+\x99q\xdb{* \x18\xf5b\x22\x18\xa2\xe7\xb6?D\
+\xaf\xbd\x18=\xb1\xc08\x85\x15k\x8bx\xb6\x11\x92P\
+]\x17\x15\x7fs\x15\xeb\x1a\xf0\xa8\x00cUe\x1b\xd8\
+\xeb\x1b\x8b*\x0b\xd9\x05\xa3\xa9WV\xf7\x9e\xcf\x87\x83\
+\xfb\xe2\xf4\x9e\xa1\x5c\xeb\xba>\xee6\xc9U,\xc9c\
+\x17N\xc5j\xed\x8e\xe0c\xf7\xc5b\xa4\x95\xda\xc8\xb2\
+ \xff\x93\xabX\x84\xa5y`^\xe2k\x04\x8e\xf9_\
+K{\xbf\x00\x16\x95\xb9\xaf\xdd\xb7$\xb8:Q-\x06\
+\xfe*)\xfc\xc2\xaaM\xae`\xa1\x95\xb4*r\xa0\xf8\
+u\x9bE\xd7Q\xa3\xc1\xa0\xe8\xbc\xe4\x92\x19U\x9e\xd5\
+\xad>\x02E2%-\x87\x8dH\x84\xb2Bks\xe8\
+]z}<n5\xcdP\xc6\x81\xe4\x9al6\x9bz\
+\x0a\xf5{Z]\xbf\xbcut$q\xde\xd2\xc2\xbeP\
+\x86\x5c\x05\x93p\x84\xc4A\xf0I|w\x81\xe9\xfe\xf1\
+\xdb\xc9\xc1\xe8\xd0\x98\xed_\x82GH\xf1\xd5\xa5\xa4R\
+\xe5\x85V\xa2\x9fJQ^\x19\xcd\xc0\xb1D\x97\x87q\
+;R\x89\xa0Z\x08\x8d\xf6\x9b\x00~\x80Q,\xb8b\
+\xf0\x181\x09\xe4\x80\xa77\xa0\x04\x89\x13\xfc4\xfc\xca\
+^\x18\x0a\xdf\xff\xc38`\x12\xe2\xa1eK\x0cb\x99\
+&\x86q\xad8\x96)F\x8c\xc6;\xd2\xb0\x19\xe0\xfe\
+q\xe0e\x0d\xcf\xb0\xe30]&\xf0\xdb\x09\xd0\xa8S\
+`zJ|\xbd\x0f\xf2\x89\x80\x01\xa7\x82k\xf5^\xa1\
+\xf7\x9b;\xf8\xc5\x0a\x83<-\xef\x9c\xf8r\x99M\xb6\
+\xabu<\xb9_\x7fI\xb7\xf7\xeb\x0cO\xb3T{y\
+\xf08D\x11\x08\x90\xee\x90\x83\xb5\x96\xa0\xbb\xec\x0e\xe8\
+hMX\xeb\xc0\x83\xbb1N9\x80TI\xdd\xbdC\
+<\xde)\x9eC\xc7xd\xe0\x1cO\xe8 O\x05\x1f\
+U\x1d\x97\x95-X\xaf\xe3\xa2\xa6\xea\xb69\x85=\xe7\
+\x19Zg\x96\xfaN;5N2t\xb3\xdb7N2\
+tS\xe2\xd7\x02\x97\x926]xN9\x13@\x96\xb4\
+\xe7\x85\x07\xa7\xc3\x0e\x84\xf5\x1dp\x082x\xf7x\xda\
+)\xb3c\xc3B\x0656\x18\x19\x8a\xc1\xa1\xf8\xef\xdb\
+\xba\xc0\x96\xbfB\x0a\x0f\x9e\xbf\xc5oQ\x91\xa0\x86\x8d\
+\xc5\x10<\x88\x7f\xa6\xaa\x1c\xcf\x85\xeco\x9c\xe7\xe5\xb6\
+\xfa\xfe\xe3\x0e!\x80'JE\xfdm\xa4\x8d\x93k\xb8\
+98#\xcf\xdf\xdb\x15W8\xbc4\x9d\xe5y\xb2\xfc\
+\xa4\x19ltUS\x1cA\xe3x/\x13\x0c\xc4\xd6\x97\
+\xf7\xc2@\x9c\x8a\xce\x81\xa0zj\x86\xba_N\xca\x9f\
+\x9cX\xbf\x9b\x11\xf7\xcd\x1e=\xb4Y\x7f\xf3\x8b\xb7\x1a\
+\x0f\x95 y\x17x\xabg\xe4\xddo\xecbF1#\
+\x019\xc7\x0f\xdfY\xa6\xf5\x0e\xef4\xce\xe5Y\x0f)\
+%\x1e\x8b\x83\xb3^\xcd\x0e\x8f\xdf\xb5\xb0\xfe\xe8\xdf\xbb\
+\x09\xae\xf71x3\x07\xba\xd2x\x97\xc9\xf8\xe9\xee\xf7\
+\x0f\xbdw\xb6\xde\xdaz\xb6\xb0\xc0!+QH\x8f\xc6\
+\xe1\x9e]k\x12{\xbd\xec\xce\x0c\x94\x90\xd4\x82kV\
+\x0c\xccg23\xd3\xe6\xc0\x04\xac\x02\x90^J\x05&\
+\xe0Qj)\xdd\xf6Puu\xa1\x05u0`\xb7\xc4\
+y\x87c\x02;\xe7- \xb0k\x9d\x1e\xca\xa1\x9c\xb4\
+\xa05\xc7\x95'\xaf]\xb6\xa2\xee\x02\xc4>\x19\xd0\xc4\
+Q\xc0\xaa\xc1\x0b\x8a\xbd\xca\xa0\xb3\x8c=cX\xd37\
+\x06\x1d\xa0pj\xba\xab\x96X.[$\x86\x83\x91\xc9\
+[Xj\xcb\xa5\xe37\xa4\x09\xee\xb8\xb1!\x16\xf7\x8d\
+TL\xccQ\x84\x86\xff\xbaK\xd0O\xd8\x92A\xd6C\
+\xc0\xa7\x18!\x07\xe5\xfe\xc4\xfb\xeer\x0f5x\xc2|\
+\xe5Q\xdb+\x0e`\xb1\xc1\x04l\xef\xdex\xdb\x17\xda\
+\xda\xb84\x17\xcc\xc1\xddm\x0ctpk\xabkS'\
+\xa1j\x88\xe9\xb5g\xa7\xe2\xa3\xf76>\xc3\xe6\x12{\
+\xdc\xe6\xdaa\x9b\x1b\xe0\xdd\xee\xfez{\x18\xdc\xe2\xfc\
+&js\x8b\xdb2v\x9f\xf4\xf6E*\xab\xf5_\xb9\
+\x12A*\xb5\xb0O\xe2\xb1\x135\xcfy\xb6w \xe7\
+J\x22\xf6o.\x19\xf4\xf2\xd0C\x89\x0a o\x80T\
+vN9\xf3\xa4\xb2D\x7f\xd6\x04\x18\xf7\x04\x0a%f\
+\xc8}\x8az\x11\x92\x9c&~\xca9W\x8b(V\xb5\
+G\xb5\xd4\xb5{z8Q\xb7\xc0\x02\xd8\xe9\xecL\xb4\
+>N\xdd?M\xb2\x94V\xc1\xc9\xd6\x19\x90\xa8\xe8g\
+\xd8\x1e\xeb,\xc6o\x8cT\xc6\x17(\xb5\xc4\xe8\x11(\
+\xc2\xef\xa4I\xbb\x076-h\xa0\xc1\x1a\xd4Sx\xf4\
+Tk\xa0W\xa4<\xb6\xc9q\xd7v\x09OZ*%\
+\xfdR\xb3w\xa9\x94\x0c\x89\xb0\x1d\x10\x86\x03\x85\xe70\
+\x02>0\xdd!w\x17=\xd4\x84T\xcf\x10\x9b\xb6\xa3\
+\x16k2=\x8by\xce\x8f1\xcf\xc1\x9a\xd7\x03\xd6\xd1\
+\xf6`v\x01\x8b,\xe0\x11\xf5;\x85\x87\x9c\xa9\xce\x08\
+\x87h\xb8\xe5Xxk\xb4O\xa0\x0b\xce\x8f#\xe3\xde\
+\x95v&\xa0\x8d\x0b^\x1c\xebp\xd7k\xab\xbe\xf1\xcc\
+\xd74\x9e\xe4l \xcd\x13\x0f\x1d\x05X\x8aW\x9c\xf3\
+G\xbc\x1f\xff\xf9\xdd\xff\x03\x0b\xb0\x0a\x0c\x99\x9b\x00\x00\
+\
+"
+
+qt_resource_name = b"\
+\x00\x05\
+\x00o\xa6S\
+\x00i\
+\x00c\x00o\x00n\x00s\
+\x00\x06\
+\x07\x03}\xc3\
+\x00i\
+\x00m\x00a\x00g\x00e\x00s\
+\x00\x16\
+\x02\x1b\xe1\x0a\
+\x00g\
+\x00o\x00-\x00n\x00e\x00x\x00t\x00-\x00v\x00i\x00e\x00w\x00-\x00p\x00a\x00g\x00e\
+\x00.\x00s\x00v\x00g\x00z\
+\x00\x11\
+\x04\xf3\xa4*\
+\x00g\
+\x00o\x00-\x00n\x00e\x00x\x00t\x00-\x00v\x00i\x00e\x00w\x00.\x00s\x00v\x00g\x00z\
+\
+\x00\x15\
+\x01\x09v*\
+\x00g\
+\x00o\x00-\x00p\x00r\x00e\x00v\x00i\x00o\x00u\x00s\x00-\x00v\x00i\x00e\x00w\x00.\
+\x00s\x00v\x00g\x00z\
+\x00\x0d\
+\x0e\xb9\xa6*\
+\x00z\
+\x00o\x00o\x00m\x00-\x00o\x00u\x00t\x00.\x00s\x00v\x00g\x00z\
+\x00\x12\
+\x0al\x90\xca\
+\x00d\
+\x00o\x00c\x00u\x00m\x00e\x00n\x00t\x00-\x00o\x00p\x00e\x00n\x00.\x00s\x00v\x00g\
+\x00z\
+\x00\x1a\
+\x01d\xbbJ\
+\x00g\
+\x00o\x00-\x00p\x00r\x00e\x00v\x00i\x00o\x00u\x00s\x00-\x00v\x00i\x00e\x00w\x00-\
+\x00p\x00a\x00g\x00e\x00.\x00s\x00v\x00g\x00z\
+\x00\x0c\
+\x009l\x8a\
+\x00z\
+\x00o\x00o\x00m\x00-\x00i\x00n\x00.\x00s\x00v\x00g\x00z\
+"
+
+qt_resource_struct = b"\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00\x10\x00\x02\x00\x00\x00\x07\x00\x00\x00\x03\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x010\x00\x00\x00\x00\x00\x01\x00\x00\xba\xe2\
+\x00\x00\x01\x81\x8a\xd9\xf0\x94\
+\x00\x00\x00|\x00\x00\x00\x00\x00\x01\x00\x00J'\
+\x00\x00\x01\x81\x8a\xd9\xf0\x94\
+\x00\x00\x00\xf6\x00\x00\x00\x00\x00\x01\x00\x00\x89\xa4\
+\x00\x00\x01\x81\x8a\xd9\xf0\x94\
+\x00\x00\x00\x22\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
+\x00\x00\x01\x81\x8a\xd9\xf0\x94\
+\x00\x00\x00T\x00\x00\x00\x00\x00\x01\x00\x001K\
+\x00\x00\x01\x81\x8a\xd9\xf0\x94\
+\x00\x00\x00\xcc\x00\x00\x00\x00\x00\x01\x00\x00x\xec\
+\x00\x00\x01\x81\x8a\xd9\xf0\x94\
+\x00\x00\x00\xac\x00\x00\x00\x00\x00\x01\x00\x00c\xbc\
+\x00\x00\x01\x81\x8a\xd9\xf0\x94\
+"
+
+def qInitResources():
+ QtCore.qRegisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+def qCleanupResources():
+ QtCore.qUnregisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+qInitResources()
+++ /dev/null
-# Resource object code (Python 3)
-# Created by: object code
-# Created by: The Resource Compiler for Qt version 6.4.0
-# WARNING! All changes made in this file will be lost!
-
-from PySide6 import QtCore
-
-qt_resource_data = b"\
-\x00\x001G\
-\x1f\
-\x8b\x08\x00\x00\x00\x00\x00\x00\x00\xec}\xebs\x1b\xc7\xb1\
-\xef\xe7\xe4\xaf\xe0U\xbeDU\xc0\xb0\xdf\x0f%\xce\xa9\
-\xc4\x8eS\xa9r\xea\xa4n\x1e\xf7\xdcO.\x88\x04%\
-\x1e\xd3\xa4\x0eI\xf9\x91\xbf\xfe\xee\x02$g!h\xb1\
-\x00\x16P$_s\x5c\xf2b\xb6\xb7{\xfa7==\
-=\xb33\xb3\xbf\xfd\x8f\x1f\xbe\xbd:\xf9n~{w\
-ys\xfd\xd93,\xf0\xecd~}vs~y\xfd\
-\xea\xb3g\xff\xf8\xfb\x97\xd3xvrw?\xbb>\x9f\
-]\xdd\x5c\xcf?{v}\xf3\xec?~\xf7\xcb\xdf\xfe\
-\xaf\xe9\xf4\xe4O\xf3\xeb\xf9\xed\xec\xfe\xe6\xf6\xc5\xc9\xef\
-\xcfo^\xceO\xfe|u\xf5\xf6\xee~\x91u\x82T\
-\xa0\xc0\xe4\xe4o\xff\xfc\xd3\xc9\x1f\x7fxss{\x7f\
-\xf2\xd7\xab\xb7\xaf\xa6\x7f\xbe>)\x8b\xcc\x7f.e\xbe\
-8\xb1\x02p\xf2\x87\xb7\x97W\xe7'\x8a\x22\xf1\xfc\xe4\
-d:mD\xdc}\xf7\xea\x97'''M\xf9\xae\xef\
-^\x5c\xdf}\xf6\xec\xf5\xfd\xfd\x9b\x17\xa7\xa7\xd7we\
-\xd6\x8a+g7\xdf\x9e\xfem\xf6\xdd\xfc\xcb\x9b\xdb\xff\
-3\x7fy\x8a\x05N\x9f\xd5'f\xef\x7f`Q\xd2F\
-\xfe?/\xe7\xdf\xcfo\xff\xf8\xc3\xfd\xfc\xba-\xc6\xdd\
-)\xaf>~\xb9\xe1\xf1\x8e\x9e\xa7\x08\xab\xcf\x9d\x9f=\
-=\xf8\xe6\xed\xedU\xb9\xb9}uz~v:\xbf\x9a\
-\x7f;\xbf\xbe\xbfkJ\x89]\xf2\xb3J~v;\x9f\
-\xdd_~7o\xc4|\xdb\x14\xa8}\xb2\x91\xfd\xab\x0e\
-\xf1\xed\xf9\xc5\x13\xf5\xf7\xdf\x7f_\xbe\xe7\x05\x11f\xe6\
-)\xd0)\xd1\xb4\xa1\x98\xde\xfdx}?\xfba\xba\xfa\
-h\x03\xe6\xfb\x1e%\x008m\xeeU\xca\xed\xa8^\xfc\
-puy\xfdMoa\x16w\xbb\xd2\x1b{zss\
-^1}\xcc(w7oo\xcf\xe6\x17\xcd\x93\xf3r\
-=\xbf?\xfd\xe2\xef_<\xdd\x9cB9\xbf?\xef\xb0\
-i\x98\xde\x9d\xcd\xde\xccW\xe4>f.\xf1\x9a};\
-\xbf{3;\x9b\xdf\x9d>\xe6\xb7\xcfw\x0d\x1c\x17\x19\
-\x97\xe7\x9f=\xfbj\xf6\xe3\xfc\xf6\xeb\xe5\xef\xef/\xcf\
-\xef_7\xb7)\x16?_\xcf/_\xbd\xbe\xaf\xbf\xbf\
-k\xac\xe5\x0f7?|\xf6\x0cN\xe0\x04)N\x1eo\
-\xdc4\x9c/\xaen\xbe\xff\xec\xd9w\x97w\x97/\xaf\
-\x96\xe2\xe6\xd7\xb3\xe6r\xfarv\xf6\xcd\xab\xdb\x9b\xb7\
-\xd7\x8d\xb0\xeb\xf9\xf7'\x9d\x87\x1f\xb5z\xd1\x96\xb6Q\
-\xe8\xcd\xed\xfcn~\xfb\xdd\xf2\xf1G\x00^<\x15\x1b\
-\x0a\xd3\xe2\xd6\xa3V\xdd[b\xabO\x9d\xdf\x9c58\
-4L_\xddL\xaf\xe7?\xdcO\xdf\xcc\x1at\x9b\xfa\
-\xfb\xd7\x1a\xdd\xcb\xd9]Cw\xfa\x8f\xbb\x86\xdb\xe9\xf9\
-\xec\xbb\xcb\xf3\xd3/\xe6w\xdf\xdc\xdf\xbc9\xbdk\xda\
-\xfc\xcb\x9b\x1fV\xa5\xde\xbc\xbd\x7f\xf3\xf6\xfe\xeby\xdb\
-l\x96\xe2\x1b\xd4k\x15,o/D=e\xae2\x98\
-/\x5c\xc1\xf4\xe2\xf2j\xbe,\xe3\xe9\xeb\x9bo\xe7\xa7\
-o.\xaf\x1b\xc8oo\x9a\x8b\xb3\xbb\xd3\x9b\x1f~|\
-5\xbf>m\x9e\xb8jq<\x9d\x9d\xdd/\x9a\xe8\x8a\
->o\xae_\xbd\x97\xf5\x0f\xe7o\x1a;3/\xfa\xde\
-\xdb?\xd6\xdb\xbf\xfb\xed\xf9\xfc\xe2\xee\xd1\x1a\xdak\x84\
-6\xf7\xe9\x917\x0d*o\xe6gm\xa3\x5c\x01\xee\xfe\
-\xc7\xd6\x06W\xc9\xf8\xfc\x9d\xfay\xf3uc.S\x85\
-\x93\xc6\xc3A\xfb/\xae\x11\xfc\xd8\xd4^{\x03\x16\xf7\
-a\xed\xfe\xbf>{\xe6\xb0\xce`U\xec\xf4\xe6\xf6\xf2\
-\xd5eS\x13\xbc \x92J\xbaP\xab\xa3\x03!=;\
-9\xfd\xe9\xea\x87\xa6\x0b\xfd\xbe\x9d\xdd~3\xbf]\xe1\
-uw\x7fs\xf6MK\xfe\xfb\xdb\xdb\x9b\xef\xf1/\xf3\
-\xeb\xa5:\x0d\xef\xf9u\xd3\xcego\xefo\x16\x19\xb7\
-\xf3\x8b\xff\xdb\xb6)x\xfc\xf5_\xf5\xd7{\x9e\xbf\xbb\
-\xff\xf1\xaa\x81\xea\xd1\x0b\xbcxp\x02\xbfi\xac\xe8\xcd\
-\xec\xfeuK\xb2|\xae\xfd\xc5\xac\xfel\x99\xd5\xe4\xfc\
-\xe5\xa4\xed\x1f\xa1\xc0\xc9W'\xda\x5cMuq9E\
-*\xda\xc9^\xe4>\x91\xfe\xeb\xa4eP\x057\xed\xe8\
-jz\xfb\xf6\xaa1\xef\xef\xe6\xd77\xe7\xe7\xbfi\xfa\
-\xa5\x9bo\xe6/~\x05\x8b\xbf\x87\x9f\xd3\x85{{\x81\
-\x05\xde\xdc\xfff\x09\xcf\xb4\xe9\xd4o\xef_\x5c7\x9d\
-\xfao\x1ex6=\xda\xf5]\xe3\x8a\xbf\xfd\xecY\xdb\
-\xf4\xe6\xbf\x86\x22\xcfOno\xeeg\xf7\xf3_c\xc0\
-\xf3%\xc5\xd5\xe2'L\xe0\xf9\x02\xed\xd3%\xbf\xad`\
-_\xc8\x1c\x01|\xe5\xd0\x0b}\x1f\xf2\xf21\x22\xbf\x01\
-\xf8\xd1`\xffm4\xd8\x7f\xdb\x17l\x83O\x0bl\xea\
-\x82m{`\xfd\xd5H\x7f\xf2\xd5\xbe\xfe\x04?1\x7f\
-\x12\xbd\xfe\x84\x8a\xee\x03\xfch#\xffj_#\x97\xf8\
-\xb4\x8c<\x06\xe0>h\x5c\xb0\xe8\xd4e\xdf\xa0\x00)\
-\xea\xf3\x1bc\x82\x05\x8dP\xb1\xf6\xcf{\x03\x83\xf0\x85\
-\xa6\xcd\xa8d>\xbb\xfd\xd3\xed\xec\xbc5\x95\x15\xc6g\
-7WW\x0dqc>W\xdf\xcf~\xbc{\xe2\xb2\xfa\
-\x88!rc\x0e\x8d\x11\xbeY\xa9\xba6c\xda\xb0\xb8\
-\xb9\xad\x95\xd5f\xdd4\xc1\xfd\xe5\xfd\x8f/\xf0\xb1I\
-\xdc\x5c\x5c\xdc\xcd\x1b)\xf0\xac\x9aSKi\x88m\xec\
-\xb2\x07kx\x975\xae\xb3\xf6e=\xaf\xea\xf2\xbb\xdf\
-\xb6\x17\xb3\xab\xad\xe1X\x0c\xea^\xbc\xbe\x9d7\x83\xd0\
-_\xad\xf2b\x02\x7f\x82l\x95-Q\xe8\xe2\xd6\xab\x87\
-\x8c\x7f\x5c_\xde7\xa3\xcc\xb7\xcd\x90\xe3o\xed\xd8\xe7\
-?\xaf\x9b\xd1\xc7\x0a\xc5\xdf\xab\xf1~;\xbb\xbf\xbd\xfc\
-\xe1\xd7X@\x88T&\xd0\xa6\x82d\x802!,\x9a\
-\xed\xc54\xa8\x18z\xe2\xf3\x05\x9f\xb3\xc6\x00%\x0b \
-\xa6\xe32\xa71:*\xe6\xc2\xe0\xb1\xc8\xb9X\xa3\xb9\
-X\xa3\xb9m\x87\x8bNb\x98\xdcc?\xeb&B\xc4\
-4d\x22/%\x85f;\x9a\x08\x11\xcb\xb0\x89\xcc\xe5\
-\xfc\xec,\xdfa=`!Dl\xef\xb7\x90\xfa{\x84\
-\x85TX\xfa\x10\x0b\x1em!P<\x125\x17\x16\xf2\
-\xf4\x83\xb0\x80\x85\xca\x84\x8b\xa4\x13\xc5|J\xcf\x97E\
-\xc5E\xfd\xa32,\x85\xff\xd8d\xb8\x16F`\x8c%\
-\x09}\xf6,\xa8H\x88\x09-i\xa8C\xb3\xb5E0\
-\xb1>\xdb\x88]\xad\xd3Z)L\xeck\x16\xd1[\xed\
-\x17\x8b\xbf5\x8b:9\xeda\x9dk&\xb1\x13k\xf8\
-\xcd\xf1\xec\xa5\x82\xd6o/x\x00\x8f\x82H\xc29\xc1\
-\x02\xac\xc26\x99\xb6Wh\xc6\x93\xf6\x9e)\xf8\xc4\xb4\
-\xa8\x83\xf2d*YP\x08\xa5c<Q$C\x8d\x9e\
-\xac\x07J *H\xda\x93\xf9H\x14gr\xe2'\xf3\
-\xa1\xc2\xa6N\x96;\x98\x0fx\xb5\x90\x81:\xda\xc5\xa1\
-0A\x0e;\x14\xd26\xed\xd8\xe70!\xfe[\xfb\x1c\
-\xcf\x03X\x88bz\xedsBRh\x82R\x02\x12\x1a\
-\x93p/\x88\xea\x1fC\x9f\xc3Dz$\x13!\x1f6\
-\x91\xd9\xbcM\x03\xacq\x9du\x1e\xdfDH\xfbM\xc4\
-\x8f\xe1D\xa0p\x84yL\xa0\x08\x81\x89L\x0c\x0aB\
-\xd0d*\x5c\x88Am\xd5` #\xe4\xc9`\xa2H\
-*#Q5\x18/\xec\x10\x11O\x06c\xc5\x1d\xd0\xd2\
-\x1f\x0d\x86K\xba\x82\x80no0L\xfa\xfe^\x87\xe9\
-h\xbd\x0e\xd3\x11{\x9da\x8d\x81\xf7i\x22P,X\
-\x94\xc9\x06\xdb\x0a/*`\x0f\x19\xc3\xee\x94\xe1\xf8!\
-<\x03\xf7\xb7\x95C\x04h\x99\x99m\xb0\xee%\x04M\
-l>\xe5\x89\x163\x03\xcf\xf9T&T@\x82\xa1\xc9\
-\xa7\xb63&!bi\x89\xbch \xab\xd7\x86\xc3R\
-\xcc\xdd8\x9f\x1a\x8e\x171\x22\xb7e\xce\xc5\x1a\xcd\xc5\
-\x1a\xcd\xed\xa2mi*f\x0b\xeeA\x83\x17\xd0\xe8\x0d\
-^\x1c\x0f\x80e\xa8)P\x1b\xe8f\xea\xd2\xefx\x9a\
-Q\x9b\x15\x9a\xc62A\xd4\xa2H<\x99\x12\x96d\xf1\
-\xac\xc1\x0bba2\x0ey\x0a^\xd4\x0b\x07g\xe6S\
-\xec\x82R$Y\x19\x974Tiv\x88]@\xe3\xfd\
-~\x06\x0c\xb6\xf739s[\x1fRo1\x18\xe2\xd9\
-\xf9\xf0P\xbc@R\x12\xb0=[+\xa3\xf5\xf92p\
-Xc\xa3 \x8c\xbe\xd1\xa7\x9d\x9d\xd3\xd9\xb6\xee\x12,\
-\xd6D$\xaa#\x87>\x1bPy{\x19\xb4\xbdK~\
-\x99\xd1\xc3\xfa\xa8~\x094z\xfd\x92\x8d\x0f\xf3\xb4\x90\
-9\x0b\xb6^G\x0b(\x1a\xca\x83\x07\x02uOY\x0e\
-\x07\x88\x9b\x8b\xe0\xe2\xae8\x99\x9a\x15\xe5\xd0\xe8x$\
-+\xe6j\x12O\x1e)\xb5\xb8%y\xf5Hh\x85\xd0\
-Y\xa5v\xe5RP\x90\x02;\x1e\xc9\xc5\x04v\x98o\
-\xd0|\x7f\x13#\x83\x1d\x9aX\x0d\xf3\x87\x9b\xd8\xf0s\
-\xebm\xe3\xd9Z\xe1\xfa\xda\x16\x19\xac\x1b\xbe\xeb\xb8\xa2\
-w\xb9\xef`\xf2\x00\x00\x1f\xde\xe4Is\x83\xc9\x1f\x22\
-lU\x09\xe5\x09\x16\xb1D\x94E\x0f\x01\x8a\x16\x13\xa2\
-b\xa1\x19\x13$\xa7\x92\xd2\x0e\x8b)\xb1\x08P\xb5u\
-\xd4\xe2\xe4\xe0\xfed\xeb\x1a\x05\x22-\xbc\xda\xfa\x0a\xcd\
-\xc5\x1a\xcd\xedb\xe0\x83\xc2i\xba\xc3\xdc\x9a\xfa\x91\xe6\
-\xd64\x8f5\xb7fxt{!\xf5~{9\xc4\xec\
-+1\x1a\xda\x04\x0b%\x0aac\x14X\x1c\x16YX\
-\xd0\xa3u\x8e\x82X\xc2\xa5\xb9\xc7\xa4E\xb5c0B\
-\xc5\xd0\x95\xeb\xc0\x18\x09\x8a\x09>\x8ej\xd6h.\xba\
-4u\x9c#`\x11\xbb\xcc\xbc%\x0e\xd8\xcb\xcb\xabf\
-\x8d\xceZ\x95\x0e\x0c\x8a\x17C\xf3\x9d\xf9\xc2\xe0\x888\
-u\xdb\x01N\x1b\x8e\xc1S\xa8F\xd1\x8d\xe3\xf4)\xcc\
-\xa3\xd8\xb2\xee\xd7\xa1\xc30k\xfa\x95_\xfe\xe2\x17\xbf\
-x\xd0\xb3\x16\x14\xc3\xda\xf0\xa7w\x80sv\xf6\xdey\
-\x86\x0ah\xa3e?g\xea\xe1\xfc\xfd\xeb\xcb\xfb\xf9\xfa\
-\xc0\xa9\x0b\xe8\x03\xdf\x8f\x03@M\xf3>\x005-\x07\
-\xd4\xdc\x0b>M\xc7#\xc3W\xdb\xda\x7f\xfd\xe5\xab?\
-\x7f\xf15}\xbd\x1dD\x15\xcf\x8as\x17\xffZ)U\
-\xb7n\xe9z\x0d\xee\xcb/\xff\xf8{\x80g+8\xd0\
-\x1aDU\xc9^6\x9f\x7f\xfe.\x1b\x19@d?\xe7\
-\xbd\xc4-\xbf\xee\x1b!r\xe2\x0e\x90\xa6\x14\x16\xae\xb8\
-\x22Pa\x11\xaf\xaf0\xac(\xab=\x01\x9cR\x94\x1f\
-F6\xe3\xd5@\xd8\xa0\x07\xef\xa2\x87vT\xe0\x95\xd2\
-\x87\xe9J\xe9\xc3\xf4P\xa5\xc7\x0d\xa5\xd7\x03\x94\xde\x0b\
-%\xd7\xc2k\xfb\xf3Pe\xa7\x0de\xf7]\xcan\x9d\
-\xb2K-{\x14\x00\xa0Zx[\xfe>9\xad\xb1\xcb\
-\xfe\xa5\xf7\xaf\xfb\x22\x16\xd9&b\xa9\xb1(P\x0d*\
-\x90\x0a\x83<\x05\x98\xc8YTC\x0fT\xe4\xd8Pd\
-?p\x91s\xe9y~\xdb\xc4a\xcdJ\xe4\xeb\xff|\
-\xf9\xdfM\x01\x174\xf3\xffy{y;?\xaf\x0b\xd7\
-w_\xa7\xfe\xe8L\x1b\xf9\xb0\xb2\xf8yu\xe9\xf3\x93\
-\xb6+\x85X\xf6i\xbf\xbd|\xf1\xe6\xd5\xc5\xff\x9e_\
-\xaca\xb5(\xc2\xd7\x97U\xf2\xd7\x0d\xe1\xf2\x99\xd3\xc7\
-\x87Z\xcdNW\xb869\xbf<\xa6SEb\x87O\
-\xdb\xabVE\xe8St\xab\xb5\xf8\xf2\xe9\xf9\xd5Zx\
-;\xaec\x1d_\xfc\x8d\xa5\x8f\x03\xc7j\x07\x9e7'\
-\x86\xec+\xbd`\xec\xd2\x80\xa7.%\x08\x1c\xbcS\x07\
-P\x82\xd8\xeb\xea\x8e)FA\xc2\xac\x0d\xa1\x12}X\
-\xd5\xf2\xd3T\xad\x8et\xfaGA\xce\xbb\x18\x9d\x17\xc7\
-t\xa9-\xc7K\x04\xd6\xe6\xb3F\xd2\xe40\x14\x11\xc4\
-\xd0\xbey\x8c\xce\xba\xc7\x22\x0e\x826\x9f\xb6\xcb\x1f?\
-`\x1dk\xba\x7f\xd0:\xae1\xcf\xe1\xebx\x95/j\
-\x86=E7d%U\xba1\x0ec\x01Q\xd0e)\
-/\xd6h.\xd6h\x9a@(JZ\xa0\xf2\xf0\xb2/\
-\x95\xf4\x98L\xa9\xa4&\xc7b\x1e\x1f&P\x12\xd4\x9d\
-&^L\x81\x5c'\x08%<\xc0\x9f\x0f\xd7\xc2\xd1=\
-q\x98\xfa\x11<q\x1bc\xadJ\xea\x95/\xf9\x11\x8e\
-\xda\xc3\x14G\x0e\xdc+'\xee\x19\xbb\xb7yM\xf8y\
-@\xf7\x17f\xd1_\xd3.\xbb4{\xce\xa2\x8cT\xa3\
-\x9e\xda\xdc\xeb\xcd\xfaB\x16\x0b\x86\xeb\xf1\xed\xd5E\x8e\
-`\xaf\x1f\xac\x12\x5c\xec\xd8\x95p\xf4A\xa6i\xc8!\
-\x16ax8\xf1\xe3\xa2\xb6\x0cl\x12M\xb2\xb0\xa1\x03\
-\xe1\x04\x0b\x1a7I\x9e\xef6^=\xf6\xb4\x80i\xf8\
-\xfe\xea\xd7\x00 \x9an\x7f\xe4\xe4\xc1xC\xed\xd11\
-\x95\xa0\xbeP\x86\x02\x08\xc8Y\xfbQ)\xae\x80R_\
-(\xaf\xd2\x5c\xac\xd14j\x10\x14\x0f$\x1dz\xc5\xb3\
-0\x08,\x94h\x08\xadyLQ\x8bzrl\xd1a\
-\x1e}N\x88>\xcd\xca\x1f\x9e9\xa2\xc8\x8f\xbbQ\x1f\
-w\xaa\x9b~\x22S\xdd\xf4IOu\xd3'<\xd5M\
-G\x9c\xea>~\xebw:Z\xeb\x9fb\x14\xe3\xa0\xb4\
-\xc9\xd4J\xb0\xb5\xe9#\xeb\xd4\x89\x5c\x0f\xe1\xd7\xa7\x98\
-%\xdb\xbf\x98Lc\x0f\x0f\xdf\xec\xbc\xbc\x9f\xdfn\xb5\
-\x1fpI\xca$\xf8\xacyn\xfe\xa7\xd9\xdb\xbb\xbb\xcb\
-\xd9\xf5\x1f\xae\xde\xb6\xcf\x0fqh\x070\xe7_\xcc\xbf\
-\xbb\x9c\xdd?\x1e\xb7\xc3)\x8a\xd2Y\x0a\xb0\xca\x94I\
-x\xb9(`)\xf9\xa8\xf5! v\x88\xfa\xa0\x22\xd0\
-\xfe\xe1|j\xbbv\xb9\x07\x1e\x18\x18\x22\xf7o\xe6\xcc\
-:f\xe1\x22\xe1Fu3\x98x\x81\x8c\xf0\xda\xbd`\
-1\xf5\xb4\xac\x1d\x0ceAb%\xdbkRa\x10G\
-\xe3\x92\x92\x16-\x88O2\xa7X\x04\xc4M\xa2\xfa\xaf\
-B\x1c\x86\x91O\x85\x9f\x22\x970\xc9\xa4G\x0d+\xd1\
-\xdeK\x1a\x982\x86\xb7\x5cDl\xde\xec\xb6\x8eC\x9d\
-\xbc\xf2\x92*Y;\x15\xe7\x22F\x16\x9d.\xc7K\xfb\
-\x1b\xa3\xea\x05\x85\xc5\x1c\x87\xd7L33?\xf9\xc9h\
-\xff\x12'\x98\xc5\xc5\xc3x\xc2X C\xd9\x9f\xef\x8d\
-\x10\x11\xf8 BL\xbc?BYB\x12\xac\x8e\xa9\xa1\
-3J-h\x0a\xd01j\x18\x02e\xc2\x85\x1f`\xa1\
-\xa2\x9a\xbc\xd8\xa9\x02\xc58\x83|2E\xcbB\x8c\x8e\
-\x13\xcbb\x0a\x969\x0a\x1d\x19F\x07e_tH\x8b\
-\x87\xa1U\xfb\xe1,d\x81\xd5~*Mg-<\x17\
-T'\x1e\xf6k\x88%\x17\x7f\x13\xd8\x1f\x05V\xe5\xe1\
-\x99\xe4\xcc\x8f\xc5F\xaa\x85@\xb5\x8cj-\x0b\xc3\xe0\
-@\xb4\x07k\xc1D\x1e\x85\x0e\x8e\xb0\x91\x8f\xcb\xc7$\
-\x1a\x89\xc6D\xa0X\xfb\xa7\x13\xa6\x02\x16\x064\x06!\
-\xc9c\xfa\x18\xa6\x0a\x8d\x14f`\xa2\xda\xafh\x09\x06\
-B\xb2\xda~\x80\x0a#\x92\x0c\xaf:v\x14\x0a[\x80\
-C\x8b\x0d\x94\xa2\xca\xea\x93)\xc9(<|\x0b\x8b\x81\
-\xbd\xbd\x8a/\x1b}\x85\x05I\x0a$j\x85%sA\
-\x13Z\xb7\x07Sq\x0ac\xd9\xab[\x9aLu\xd9\xea\
-\x96\xadj\x99k\xa30\xd2A\x9f#\x8c\xfbb\xa4\x92\
-\x05\x0cY:\x9e7\x8b\xbbXg\xc0\x85&\xc5,\xab\
-\xe5T\x9a\xcd\x18eq4\xd5\xc8\xf9\x94\x1e`B\x06\
-\xc4\xe4vh\xc3`h\xe9\x8d=aQ`\xe5q8\
-\x0d\xfaf\x17\xa2]q:t\xcfr\xa0\x1e\xaf\x17\x05\
-\x941=\xd4O\xdf\x07\x93\xb2\x8c\xf0\xc1?\xf1>\x9c\
-\x94\xc7\xf7\xe1\xbb\xece\xe6\xbe\xbd\xcc\xba\xfd\x06\xa83\
-?;?\xf7m7\xce\x89\xc6\xb8\xbd\xcc8\xfe\x04\x8d\
->8P\x8es\x16\x00\xa3\x1dm\x7f3\xc6\xf1\xe0\x80\
-c\xc1A\xc7\x83C\xf6\xdf\xe9\xeeB\xf4\xde\xf6\xe0B\
-\xb2}{x)M\xb2\xdd7\x04\xe6Y\x9e\xcdp\xe7\
-\x0d\x81\xca\x12\xc3\xcc\xcf\xce\x9bF\x8a;\x9e4\xe1B\
-\xb6?\x9c$\x8c\xef\x85\x93\x84\xf90G%\x8c\xb7\xcd\
-\x22\xa4\xe9\xe0\xf9l\xad\x8c\xba\x85\x0cl\xd3p\x95E\
-\x92\x07\xae\x8b\xf0\xbe\xbd\x96\xc2\xb9\xc6%\x800=6\
-\xb8\xcb\xf9\xacM\xdb\xee\xe7\x14\xc1\xed=\xf1\xf9E\x93\
-\xd6\xd0\x1ca\x1c\x9c9\xe4] -\xe6\xbbz\x17\x12\
-\xc0m\x0e^9kv[\xed\xd8\x1cH\x80\xc7;\xdb\
-\xe1\x18\xb9\xcf!\x8fx\x97;rD\xf0\x03n1.\
-\xa1.\xcd\xda\xf8\xe6\xd0+\x06\x85\xb1\x1f\xa7\x1c\xc6\xe9\
-\x90\xa3\xcb\xf5\xb1\xeb\xfa\xf8\xb6o\x0c\xdc\x1d'\x1f\x18\
-#f\x84^\x8c\x92\x861\x1a;/\xb1>\xcf\xb1>\
-\x19\xb2>cR\xa7Sv\xd8\xae\x1a\xb1\xcfAq\xb9\
-\xc3>\x7f\xc3\xb88\xa7\xad\x0f\x8a\xcb\x1d6\xcb\xcf\xe4\
-\x0c\xe6\xf2\x0e\xeb\xf7y\x9a\x9e\x93F\xd7\xcf\xedd!\
-\xdd\xe1L\xf0\xa1\xf3\xc5\x87\xcf\x22\x1d>\xe1|\xf88\
-\xd4\x83/*\xce\xec3\x7f\x07\x19m\xfe\xd3\x87\xa5?\
-\x13$\x99\xac\x98;\x14\xa0\xd0\xac\x13\x09P\x10\x03\xba\
-\x93<Q\xc0X\xc1\x1f\xad\xbd\xd2\xec\xf9^\xd5\x9d\x0e\
-\xf1^\x15\x8a\x02\x18\x93lx\xb1\xea.\x1f\xec\xc5*\
-\x19\xc8A\x160\x15h\xffh\x22\x1f\xd9:&\x03;\
-\xf6:&\x9d\xe8\xc7\xba\x8c\xc9 ~\x12\xcb\x98\x0c\xe1\
-\x13^\xc6dH\x9f\xec2&C\xf9\xd0\x1b\xcbZ\x0a\
-\xb3B$\xa2\xd5\xf6\x94\x0aa\x88U\xf34.\xc8\xa1\
-\xa8\xb5kH+b!6\xec\xb0\x90\xa3x\xb8\xc4d\
-\x0aK6\x9e#\xa6\xe7Ilx\x01\x82\xc1\xfe\xd3\xae\
-V\x84\x5c\xad\x86\xb9h\xc51\xc4k\xd0\x07\x85\x0d%\
-H\xadNM\xfb\x12\x8f\xf87\x00\x22\xc7\x04$t\x81\
-\x87\xe6\x13 \x92\x0b\xfbp\xaa\x16h\x0fz\xd4\xf1\x83\
-\x97\xb4\x96f;8t\x02\xa3\x10\xa0a\x04\x80\xf6E\
-\xc0\xb4\x88\x04\x22U\x93p.\x19\xe0X\x11 .\xae\
-aR[\x88gA'\x921z\xc1\xb0^\xa9\xff&\
-\xbd\x86k6}\x5c\xb5r\x8cV\xbf'\xeaiB\x03\
-\xb5z(\xa4\xd53\xab\xbc\x06\x13n5\xc6p\xdar\
-\xf5<rpR.V\xcfCQ\xf4P\xdf\x05\x83\xd5\
-\xb22\xb1\xf5u%\xf2\xf5x\xcd\x91\xb0\x88\x10r\x05\
- \xa2D\xa0\xf3G\x06\xc3\xa0\x8f33\xdb\xfb\xdd\xb5\
-\x17\xf2$\xae-\x81\xc8\x8au\x06>\xca\xc5CP\xb9\
-\xbep#.L\xfe\xd0z\xee\xde\xdc\xceg\xe7\x7f\x99\
-\xdf\xbf\xbeiK?\xbfh%o\x85\x15\x940\x041\
-n\x91\x8a\x82\x86\x1c\xcde\x116\x0c\xf3Q\xed\x87\x8e\
-\xdaSZq\x05\xeft\x0c\x1e%9\x99+l(\x85\
-\x5c#\xaa\xfbp)\xae\xfd\x90\x8d\xd0\x96\xf2\x88\xde\xc2\
-\xa0\x1e\xd8h\x85E\x14\xea^V\xa7\x02\x04\x81\xb5\xcd\
-\xac\xd2\x9cui\x86\xc6\xe2P4\xd0(t\x82\xc5\xcd\
-\xdd\xbc\x9d\xe0\xa4b\x19F\x92\xed\x0f(\x11\x1e\x19M\
-6k\x01\x0f\xb4\x89\x96\x14LH\x19\xd3\xca\xc8\x07!\
-\x14\xc9\x9f[Y7l\x0f\xb2\x91\xc1\xc7\xf8V\xa6\x85\
-\xd9T:\xad\x8c\x8b\x0b\x19\xc7qZ\x1ao\xe3W\xc6\
-\xb7\xb4\xb0\x12di\xb5\xa5M\xd1\x8aYH\xd43V\
-W\x89\xceV\x88\x86\xdb\x1ai\x98\x81\xb7\x91\xb8\x12\x92\
-\xb8\xb4\x97A.,2\x81\x22\x08\x06\x88\x13\x06*\xca\
-):Q(\xa0\xaa\xa9\xa3\x1a\x1a\x1e\xb5\xa1\x15TM\
-\x8dN`\x07RHW\xa2\xba\x04\x0b\xee\x8c\xe7\xa4\xe8\
-\x10\x5c\xef6%v=PO\x85\x83\xbe\xdb\x04\x0e\xb4\
-X\x842\x07\x97\x03\x9c{\x9bv}a\x97I\x07z\
-\x9b\x8b\xeb\xacG,\x07`b\x1f\xd4X\xe7\xb6\xf3\x02\
-\x08\x0a<\xday\xf7$\xb8\xbf\xc6f.C\x1a\x03\xa0\
-I\xec\xa8\xb1\x99\xdb\xb0\xc6\x88\xb3\xdcYc3\x8f1\
-\x1a\x9bU\x8dW\xd8Z\xecp\xcc\xfa\xc5\x19^\xd0\xee\
-k\x144|\xfer\xf73\xa0\x19\x83+\xf3w\xe1\x80\
-\x1dNi>\xa3\x99\xaeU\xe6\x188e\x9boE\x9c\
-\x9f\x9f\xedl@\xb2U\x939\xbfx\xb9\xbb\x01\x89\x8f\
-\xd1\x98qHc\x07v\x80\x9d5f\x1e\xd6\xf8\xe5\x85\
-\xce\x01v\xd6\x98u\x84\xc6\x22v\x94ua&\x12G\
-r\x8b&\x0ac4\x86a\xb7\x188\xe7\xf9\xce\x1a\x83\
-\x0dk\xcc\xe1\xe7ko\x87\x875\x868\xf6\xe9\xd9f\
-f}\x072\xd8A\xd6\x17p\x9a\x92\xf2\xc4J\x06\xa3\
-(\xb6\x1f5\x99z\xf1@Ik\x7f`Q5\x0cn\
-C*\xa2\x12d\x88\x93\xa9e\xb1\x0cC{\xfe\x9eH\
-\xfe\xcd\xec\xbc{\xbe\xb6\x03\xb0v\xde\x85\x01\x17\x0d\xa8\
-S;\xab$\x17\xef\x92\xdc.(TSb\xc4{\xa6\
-\xfe\xe0\xad\xcf$\x11\xea\xca\x9c:\xa5\x1cQG\x89R\
-7>n3\x9dz\xf0\xc2\x8b\xf5\x16^\xa9\x0eU\xb1\
-@J\xe6Ci\x979\x08\x98)5\x0e\xd7\x82\x91\xc8\
-\xdc]2\x03\xe0\xa1\xdb\xe8\xd5?\x96\x1b\xde\x1e\xedE\
-\xc3!-Z\xab\xe3\x00\x13\xa5\xe7\x1f\x12);\xc8\xdb\
-o(\x82\x94\xc04\x81b\x98\xac\x12\xcf\xfbq\xd9\xb9\
-n\xc8\x22b\xa5n\x1c \xf3C\xe2\xe4\xf0\xb1\xe3\xc4\
-%\x0dA*LQ\x885\xc5?(L\xf43L\x9d\
-\xc0\xad7\xa6\xab\xaf\xe4T\x0b\xb7\xba\xd6\xed\xed\xf5\xd5\
-\xcf\xda\xdd\xe5:\xb6DF\xf1\x0f\xefqM\xb8W#\
-\xa9{^\x8c\x0ap8Rw&,\x91\x1d\x1f\xd4Z\
-#i\x15\x85\xe2\x14\x19z\x10\xfb\x99\xf0\xc1\x9d\xa8\xb9\
-\xf4*\x1f\xd0\x99\xb1)\x90\xe6\xaeu\xf6\x14\x8a\x06\x98\
-x\xb5\xc1,\xc9\xc2\x5c\xd7\x91D\x14c\x96\xf8\xa8\xba\
-Q\x0a\xdc\xe5p6\xeb,\xbf$+\xc9f\xab\xcb/\
-E\xdc\x8d\xaa\xceT\x802T>\x9cFL\x8c\x87\xf1\
-Od\xc4F9\xc1\xba,O\xb3\x10i\x80w\x0fH\
-Mg\xc2Z\xf3\x95\xe6\x11\x05\xb1B\x14\xa1\xf9AQ\
-\xe0\x03\xa1\xc0L\xd4\xa2P\x12\x15R:h jq\
-3\x8f\x8a\x06\x16\x08I\xa8\x11$c\xe1\x0e\x0d-=\
-8#\xe9\x81\xc1`b\xef\x05C\xb8\x9e/hPR\
-\x94\x14\xab\xeb\x02-\xc1\xa0\xb5\x07i\x88\xaa\x0d\x0f\xe3\
-\xf8\xe1\xfc\x10y\xc0.\xed\x15\x8a\x00\x81UOeR\
-\xc0\x0d\xa5\xbe\xd4\xa845\xd4\x87\x92F\x84r\xe0\xe3\
-\xf7(\x13zO\xf1\x87<\xc0j\xda\x82 \x10\xa1\x13\
-(A\x01f\x8b\xf7\x0bf\xe2\x92\xd6^F\x183\xf9\
-\x04\x13\x8b\x88\x11O\x8c\x0a)'\xd8P\xf8\xd1\x8e\xf5\
-\x8c\x0b\x08\xbak}G/%=\x05\xa2\xbe\x93\xe4\xa2\
-\x8a\x84\xf5]\xbfb\xd1\xa4\xcc\xfa\xa19.\xe2\xe2F\
-\x0b\x80\xcf\xae.\xdf\xfcuv\xff\xba\xbd\xf9x\xbdy\
-\x12\xff\x91\x8a\x91\xda\x1d0o\x9a\xcb\xee\xd0\xbf]\xcf\
-{\xf5\xe2\xed\xed\xd5\xaf\x7f\xb5\x8a2#\xf3\xf3\xdf\xb4\
-w;\xb3\x00w\xf7\xb77\xdf\xcc_\x5c\xdf\x5c\xcf\x1f\
-\xae\xa7\x8bO\x12\xbc@+\x92\x0a\x00!\x8f7\xda\xfa\
-lj\xfe\xc5\xed\xcd\xdb\xeb\xf3n\xe6\x7f\xdf\x5c^\xaf\
-\xe6~{\xd9\xac\xeb\xbd\xbal\xfe\xf7\xe2\xe9\xf1\xf3\xd9\
-\xdd\xeb\xd9\xed\xed\xec\xc7\x07i5w9\x07\xf1B\x0a\
-\xeaC\xf6\xda\x17\xa1\x1a\xbd\xffr2\xc5\x90\xe2l\xa2\
-\x13\xf2\x92\xc2\xae'\x9f7\xb9)%]\xdd\x9er\xf1\
-dJ\xa0\x85@\xcd'\x8c%0\xd0\xad\xc9D*\xe9\
-@6\xe1,\x1a\xe4F-\x03\x22(\xe9.1\x11/\
-\x1a\x99\xc9m\xa6\x94\xd0\xd4\x89F!\x22\xc3\x87<\x83\
-$\x9fX\x14\xf7\x14\xc8\x93\xaf\xba\xd9i%\x88@\xb0\
-e[\xb3\x11\xa0\x84\xb3G\x9b\x89\x85U\xbc\xcd\x94\x82\
-\x14\xd4f\xa2\x17\x86&\xd5\xcc\x96/*\x15\xb4\x8c.\
-m\xab\xafDA\x0c\xe4.\x0b\x14)al\x5c\x85u\
-3k\xc1\xbe\xeafW5>_f\x1b\xb2<iL\
-KY\x92*\xd8\x85\x06\xd5\x8a\x02\x99\xac\xc2\x88&\x85\
-\xcc\x89\x9e \xc7&\xd3\xa5H:\xf2c\xe5H\xbe\xb7\
-\x1e\xffu\xb2R\xbf\xc2E\x02\x01e\xc1\xd8\xa3p:\
-\xe4S\xb6\xb6\x99T@\x02}\x22Z\x12\xbdek^\
-\xd0#p\xa2P<\x9c\xd4\x96\xe5\xa2\xc2\xe6\x9c\x13\xd5\
-\xa2\x19\x16\xad\x0aY\x92\xb2!5,\xc1\xccA\xcb\xcc\
-\xf6\xa9E\xf5B\x02\xd0\x02.\x82,\xea\x04\xd8\xc9\xfe\
-\xfc!\x1b\xc5\xed\x89\x05\xb6\x99^\x10T\x1fEy\xb6\
-yT(\x05\xe2\x9db\xa5\x17!qzP R\xdb\
-L,\xe0\x1aO\x9a\xf2\xfbQ\xf9Wg\xba\xae\xf5\x00\
-\x8c\xcb\xc8\xa7\xbbI\xe2\xfa\xe6|\xden\x94\xb8k|\
-\xc6\xdd\xd9\xe2\xef\xee\xe1\xbf\xe5\xcc\xde\xa3'9\xba\x9b\
-\xaf\x0eh\xbc\x9b\x87\x22i\x08j\x13(\x22\x90A\xd9\
-\xfavg\x03Mi/\x97\xce\xdf&\xd3\xcc\x82\x82\xe2\
-\x93XDj\xa0\xb1\x8d\x9bw/\xca\xcc\x8e\xef\xbay\
-\xac\xeb\xd5\x22\x0a\x08\x05\xe4\x87p\xf3\xac\xd5\xcdoY\
-\xbf\xeb\xd6\xe1+\x8e4\xbd0a\xe0\xaa\x1f\x05/j\
-\x88\xd4\xf5\xa3\x88^<\x11\xb8\xebG\x91\xb4\xa8\xaa\xd1\
-;\x0e\x80\xb9\xa8Q\xc8\x8a\xb3`/\x22\x12V\x1d\xe9\
-2\x13S\x8cW\x1ci\xcd\xee:\xd2\x9a\xbd\xea\xdb\x98\
-K\x0a\xbc\xe3\x05)KDp\xae:R\x93\xe2\xaa\xc4\
-\xba\xeaH\x0d\x8a\x03Kj\x97\x85\xb6\xc5\x8dL\xad\xc2\
-\xba\x99]GZ\xb3W\x1d\xa9\xb6\xc5\x0d7\xedzR\
-\xc3\x02\x01\xee\xda\x05\xc7\xb2@\x04\x00\xae\x02\xe9VB\
-I\x03\xbb\x9e4\xbc@\x90\xa4WOZ+r\xc5\x91\
-\xd6\xecU?\x9aP2H4\xba~4\xa4\x18\xb3\xb2\
-V?\xeaY\xdc\x04\xddV\x1d\x96KI%\xd7\x8e\x1b\
-u*\x8a\x8a\x9e\xd5\x8d.2E=YV\xdd(\x12\
-\x15Tg\xef\xba\xd1\x87l\x00\xa4\xae\x1bE\xccb\x09\
-a]?\x8a(%<E\xde\xf1\xa3\x08\x05\x10 V\
-\xfc(p1C\xb4\xeaH+*+~t\xdb\xd8)\
-?\xbd\xd8\xe9\xdf\xe5\xe5\xf3\x83zy\xc4\xe2d\xc1\xfa\
-\x11\xb8\xf9\x8fk%;\x11\xd9\xff\xf7K\xb9\x8d\xf2\x90\
-K\xb9\xc7o\xb4Kz\xdce\xe7\xa4\x13d.\x00@\
-\x13\x82\x12n\xd4j\xbaDW\x0bv\xbei>\x85B\
-(Q_<\xc6\x93\xc2\x8f\xca\xf4|\xee?\xa8\x7f\xfd\
-\x86\xd3\x17\xc8_\xac\xbde\xee\xfbf}H?+\xd6\
-\xdf\x7f\x8e\x9f\xbf\xcb\xaa\xb0*\xf7\xf3\xb3~~\xf0e\
-*\xc4\x1a?\xc3\x94~~\xb1\x81\x1f\xc4\xe7\x00k\xfc\
-\x5c\xbd\x9f_\xc2&~\xfe\xfb5~\x0f.\xb7\x1a\xcd\
-.\xab\xc5\x80zv\x99\x83\xecp\x98\xd2\xf9|>t\
-\xdaI\x97\xb5\xedp\x84\x07\xcdc~\xbe\xb6rb\xc4\
-\xfa8\x83}6\xdd\x1b\x1d\xf6\xf0\x97.k\x19q\xb6\
-\xd4h8R{4N\xdf^c\xe3y\xee\xa0q\xe6\
-\xbfQc\x91\xdc\xc3\x00Dq{8`\xf1\xb7\xfd\xe1\
-b\xdc\x03\xc7\x00\xeb\x0a\xc7\xa8c\x17\xc8Y6\x1f\xbb\
-`\xd2\x9e\xba@\xc5\xda?\xef;{\x01)\x9a[&\
-\xfb\x1e\xbd\x00\xf5\xe9\xbd\xce]\xa8\xdb\x18\xa1 Ag\
-[\xa7qAM`]y\x87\xeb\x085\xf2\xa94\x83\
-\xeb{@\xb9\xfd{\xecP\x093E'\x88\xc5m\x22\
-%\x93\x84l\xcc\xb1\xc9\x1c\xc3{y|\xff\x03N%\
-\x0a!\xa1b}e\xa2E-\xba\xf0X\x96$\x86\x88\
-\x95]\x06\x9cd\x1f\x03<0\x1a\x9e\x9f\xb0\xf1x\x0c\
-\xa2\xc3\x86{\x1b\x8f\x14\x0a\xef\xc2\x93\xd8\x96\xda\xc1\xba\
-K\x0d\xea7\xf5\xdb\xfb\x1eJ\xb2\xc5fi\x82\x09\x8c\
-R\xdd\x06U\x17\x80\x9f\xa6\xea2B\xf5\xc11W\xdd\
-\xa6\x93X\x02\x14:\xc3U\xf5\x02\x1a\x8aR\x07`Y\
-@5 \x9f\xc6\x0d\xca\xc5\x18hx4V\x143%\
-'^X1\x89s>\x8d\xc94\x0a\x18\x93\xb0\xb6\xbf\
-\xb0\x90;!\xcadJP\x88\xc4\xc9\x9aK.\x99f\
-\x9c\xfb\x0f\xda\x88\x9d\x86\x9b\x8e\xc4\x9e rQ\x0f$\
-\xad{\x0b\xa5(#i\x1d\xc4b\x97\xe4\xec]\x92\xb3\
-.\xc9\x00\x90V\x14<\xc3\xf2ax\xab\x08\x89>\x99\
-\x1a\x97\x16<\xc3\xc9\x94\xa9\xa4\x03\xa2\x8eB\xec\xa8\xae\
-8\xad\xb01hg\xe5R\x14\x03\xc3\xce6B\xa6\x02\
-\x8aa\xb5\xd5\x09\x17\xcf\x04\xb2=}\xb1\x17\x1533\
-?\x8c?\xb6m6\xb6\xcb\xdeF\x85j\x1d\x9bB+\
-\x10VM\xca\xb1\xb8+Q\xdd\xad\xda\xa58\xebR\x0c\
-O\x22\x98B\xa04H\x91a*\xd0d\xda\x82\x06\x98\
-!\x13.\x9c@\x086A/\x0c\x09\xd6\xdcV/\x96\
-L\xe3\x1a\xa5\x0d;u\x8b\xdc\xdb\xa9\xb7pdp5\
-\xb1\xb4\xc2\xceH\x5c\x9d:A\x11\xf2\x1a\x0cU\x9aA\
-\x13CwR\x89\x87f\x08\xee\x84\xe1\x13+\xaaJ)\
-6\x99JIAs\xc0Q66\xec\xfd\x15\xf1\xa7c\
-c\xcc\x07\xb61\x1a\xb61\xdb\xdb\xc6(J\x0a%\xd5\
-5Jb\x85HS\xde\x99\x89\xe5\xf0\xea\xc6\x1c\x8b\x91\
-\x09\xc8\x9e6\x86%\xc4\xc8\x95\x0fec0lc\xc0\
-?w\x8e\x151\xe1m:\xc7\x9f=\x7f/~8\xd6\
-\xf3\xff\xf4[%\xc76\xad\xf2g\xcf\xdfgc\xac\xc7\
-\xf0\xfc\x87\x8c\x0a\x0e\x14\xa9\x8c\xb11\x1a\x19]\xfc\xf4\
-\x83|\xda*\xc8\xffy\xf8\xbd\xb1-\x92\x8c\x18~\x0f\
-\xb4\xc5\xe1\xa9\x97\xf1\xb3<c\xec\x07GL\xdf|\xda\
-\xaac\x1cU\xf5\xf1\xd3\xb0\xe3'\x85\xc7\xa0\xa3\xa3f\
-s\x8f\x8f\xce\xf87\x0a\xa3\xe0\x19\xf1\xa6\xe4\xa7o<\
-\x90\xc7@\xe7\xdf\xe9.8$\x8e9\xc7\xefZ\x9c\xc2\
-\xb9\xb3\xad\x0e\xa28\x88p\xd5\xc9\xaaBT\x1c\x00M\
-\xc7(d\xc3\x0a%\xef\xab\x10e\xd1`\xd4\xce\x89\xc7\
-Y \x08\xbdV\x92[1[=!\x0a\xad\xa0\x90\xd1\
-\x18\xbd\xb6\xe8\xcd\xc3\xf6\xd5+l\x11\xe8zV\xe3\x83\
-\x22\x18\xa8T]NafJ\xc4\x07\xbd*\xcd(\xbd\
-h\x84G\xfeX\x1b\x15|\x98\xb7G\xc1E\x11C\xad\
-\xbbu\x89\x04\x11\xb3\x86\xafV\xc8\x05\xb53\x88\x84\xa2\
-\x16.8|\xf2\x06QF\xda\xc3\xb8\x8a\x1c\xc0\x01'\
-S\xa2\x92\x14\x99\xb6\xb8DO\x8c\x18\xb1\xb8/8~\
-~O\xb4#bv\xc4\xa90)\x9a\xec\x150\x91\x82\
-\x99a^\xad\x8c:$gk$g\x1d\x92\xe1/\xec\
-\x98\x01\xa9L\xa0hcNE\x83\x04\xd4'R\xc4)\
-\xd4c\x82$\x85\xcd\xbd\xc1\x0e#\x0b\xa59\x8f\xc2N\
-\x86\xb1c\xde\x13;/i\xd1\xc1\x8e\xa3\x88\xb0q\x17\
-\xbajh\xf5v\x076\xd2\xe1v)\xc8\xd1\x02\x02\x05\
-\x0d\xd84\x1b\xf8\x048\x09y\xc2\x85=\x0c\xad\x9d\x12\
-\x83\x82\xc9\x82\xb1\xf8\x96\x13\xa2j\x8e3;\x1a\xf6k\
-d\x1f\xd7\xccE\xf7 \x17\x19u\xa6v0l=+\
-1\xder\x22\x8b\xa2{R\xd7tB!\xb2.v_\
-\xa59\xeb\xd2\x0c\xda\x10e\xaa\xb0\xb66\xe4\x0a\x94\x18\
-\x13,\xa0\xee\xc4\xd6\x5c\x05\x13p\xdb\xe4p\xe1\xd3 \
-'S\x8a\x22\xe4\x068\xc6\x84h\x8bx\x93\xe3g\xcf\
-\xf5^\xecl\x1b\xcf\xf5\xf3\x0b\x90^\xfcd\x9b\x17 \
-?\xe3\xd7\x8b\x1f}\xc4K\x07F\xf8u\x82m&\xe4\
-\x7f\xb6\x8b>\xbb\xc0\x18\xf1\xd2\xe7\x13\xc4O\x0f\x8d\x9f\
-}\xc4\xcb%\x8e\x1fV\xa1\x1c\xe6\x9dk\xfd\x5c^\xf7\
-\x8bx\x0fN\xeb\xf5\xfc\xf2\xd5\xeb\x86\x1e\x8bf\x90\xe9\
-\x12\xce\x87=Y\x99\xf8\xb4\x85n\xb1\xfb\xb3\xa5K\xe0\
-d\xc0e\xd9\x16t\xa2\x98\xa6\xe0\x1b\x8b\xb2\xf6\xfd\xbd\
-\xfe/\xe8\x91\xbc\xf7\xeb{XX\xdc\xd4\x16\x05\xdf\xa8\
-t\xfd\xfc^\x8f\xf6\x16\xf8\x8e\xf6\xa8\x9eB]\xed\xc1\
-#\x1d\xc5pU}\x12\xf1\xa0\xae\xfaH\xc4\x89\x07S\
-\xdf\x82{>>\xc8\x84\x892^{e>PY\x95\
-\xb5\xa7\xaa\xc0\x00Xu|a9\xed@\x85\xe5\x8c\x1e\
-`%w+\xe8\xaa\xa3\x1a\xf2\x9e\x18l\xb8\x9c,\x9a\
-b\x11\xa34\x9fO\x97>\xd4h1\x0f\xe2\x854\xb6\
-\x0d\x82\xeb\xd1\x07D\xa9_\xe3\xd7O}\xc0\xd4\xc0\xa3\
-\xb0\xd6a5[\x16%\xa1\xc7\x8d\x97\xa4\x5c\x00\xa3n\
-\x0c\xae\xcf<\xf5\x0bdZ8{>O\x1e#w'\
-\xf5lNB\xd8\x7f\xa7V\xe5;{\xf1\xed\xe5\xf9_\
-\x9bM\xe7\xf7\x7fkn\xeeX\xd0a\x0eEG\xf0\x18\
-T\xeb\xcb\xc5\xdfV{,\x87{\x9e\x90\xc9\xd4\x5c\xea\
-\xcb!s\xa9\xd3\x07\x1c\xc5\x9d\xb9s\xbcY\xd6I\xf9\
-zw\xef7B\x89\xf2^\xe3\xa1D\xeb\x07\x80\xacM\
-C\xa7D\xf7\x18\x10%F?k\xc66\x0d\xb1.\x88\
-$\xbd\xfci\x83\x05\xc9y\x9b\x06\xf9\xb3\x8b\x02\x00\xf6\
-\xcb\xa0~\x19\x0am\x1a\x94!\xc4\x90\x99\xd9/C6\
-\xc8\xf06\x0d\xcb`\x8b\xcd26T\xb3I\x9b\x86e\
-H\x0e\xe8\xb1\xa1\xbe]\xdb4,\xc3@{\xf9\xf3\x86\
-\xfa\x8eY\x9b\x86\xf9;\xe2F\x1dxC}\xcf\xb8M\
-\xc32\x02e\xa3Mq\x7f}\xdf]^}7\xbf\x1d\
-\x16\x91\x88\x9bEl\xa8\xee\xf3\x8b6m!Cx3\
-T\xb1\x01\xaa\x8b&\x0d\xcbPt\x09\x8f\xe8\x95!\x1b\
-\xaa<\xa5M\xc32\x98t\xa3\x1eB\x1b\xcc\xf6\xacM\
-\xc32T137\xe8!\x1b\x9a\x9f\xb6iXF\x88\
-\x0a\x98\xf7\xcb\xd8P\xe7Jm\x1a\x94a\x10\x06\x00\xd4\
-/cC\x9d\x0b\xb7iX\x86\xa0m\xb4]\xc5~\x19\
-\x9cm\x1a\x96\x91\x14\x1aD\xfd2x\x83\x0c\x1eV\xc2\
-9}c\x85\xab\x8e\xee\xfb\x22#2\xd3\xfbe\xf8\x86\
-\x06h\xb3\x98\x0d5\x8e\x9eo\xef\x8c;\x84d8\xfa\
-\xb9]~\x92R\xc5\xac\xb3v\x91J\xa6\xad\xac]\x8c\
-\xe2NJ}\xcb\x06\xc3\xed\xfd1Mxl\xf0~\xd9\
-\xa6}c\x9a\x88M\x87ax\x9b\x06\xeb\x15$\xa5\x9f\
-?\xf5\xf3\x7f\x99m\x1a\xe4\x8f \xd9\xcf_6\xd8\xcc\
-\xbcM\xc3\xfc\x85\x09\x93\xb8_\x86m\x90\x91M\x1a\xd6\
-\x81P\xad\x9f\x7fl\xb2\xfb6\x0d\xf3\xb7\xb4M\x9dB\
-\xe4\x86z6o\xd3pl\x99\xb1\x81?\x1d\x80\x7fn\
-\x8c]#7\xc5\x95\xd2\xa4mb\xbe\xe0\xcd8m\xa8\
-k\xc16\x0d\xcbH\xd5\xcd26\x8d#\xb4M[\x04\
-\x01\x19\x91\xda\xefK\x01F\xfbkuHS\xe2~\x19\
-\xb4A\xc6\xbcM\xc32R\xa2\x9f\xff\xa6\xfa\xbeh\xd3\
-\x16\x9d\xb3n\x0e\x00`C};\xb4iXF\xf2\xe6\
-\x18\x1cb\x83\x8c\x97m\xda\xa2\x7f\xd6\xd8\xd8?#l\
-\x90q\xd1\xa6a\x19\x03>$\x91F\xda\x14\x1ek\xee\
-\xc1#\x0b\x12\x81o\x98\x810\xf7\x12\xee\x80\x87\x9c\x87\
-\x08\xa3\x9e>\xdbd\xf4`\x1e`C\xa7a6\xda\xe1\
-\x22K\xf4\xf3\x8f\x0d\x1d7\xb5i\x8b\x81#\xf7;s\
-\x87\x0d\x9d\xdelx\xe4k\x94\x1b[D8m\x10\x80\
-m\x1a\x8eX\x95bc\x8f\xe4\x1b*9\xcf\xdb\xf4\x11\
-G\xacV\x80X\xea\xd2;\xc5\x82\xc0J\xefD\xac\xcc\
-\xd1\x1b\xb1\xaa\xf4X\xbf\xda\x86\xb0\xf2e\x9b\xf6\x8eX\
-u\x83aR\xb4i\x9b\xde\x937\xf5\x0aa0\xa2^\
-G\x9fd\x97\xb2\xcf\xb9ni\xdbO\x9c\xf3\xffk\xef\
-\xca\x9a\xdb8\x92\xf4\xf3\xfaW0\xfc\xa6X\xaa\x94\xf7\
-\xa1\xf1:B\xa6\xad\x89\x89\xf0\xecN\xec<\xec\xa3\x83\
-\x92@\x8df$\xd3AQ\xbe~\xfdVv\x03l4\
-\x89&\x05\x8a\x1c\xcb\x07a\x19\xa8\xaf\xd1\xd9\x95_\xe5\
-U\x05\xa0\xcb\x8f\x85\x9e\xbf\xff]\xce\xe2\xfd\xd7\xce\x9f\
-\x9d\x9c\xe8\xd55\xf9\xdb\xdf\xe4\x8c%n\xc1\x06+\xec\
-q\xd3\xbf\xdeg\xdc\xd1\xe5\x05\xc9\xb4\xcf=\xce\xe4\xc5\
-\x9d\x92ax\x1b2\x8c\xdf\x9f\x8c\xd5\x8b\x93\xda\xc4\xf3\
-=M\x83M\x17\xd8X\x12}\x977\xc0\xe3\xb0\xdb\xd0\
-\x11q_w@\xe4\x84_\xf2~\x80\x00\xb7\xa0C\x80\
-\xf6\xb0\x0e=9\xd9\xe3~\x80 \x0bt,\x8a\xbeK\
-:\xc8nC\x07\xeda\x1d\xe5\xdf{\xd0\xc1\xb0_\xe8\
-\xb8[:8nC\x87\xc0>t\x10\xeeA\x87\xec\x15\
-I\x09\xef\x96\x8e\xe4\xdb\xd0\x91z_\xb1C\xd2\xffM\
-\xb1cY\xe7\x05\xae\x14\xf8\xe6}\xc9\xf5\x04/\xcf\xf7\
-o\xd8\x7f\x97\x15\xdecWi\xe1\x14~~\xbdd\xbc\
-*\xd9?\xc06\x14o\x93f\x15y/W9\x86\x17\
-\xefk\x1b\x8a\x0bivY\xf4\x1d\xba\x8aY\xde\x82\x0e\
-s\xbc/W1\xe7\x8f\xd5U,\xf2\xc6\xdd\xb9\xe1\xe4\
-$`OW\xb1\xac\xba\xf1\x16\x92\xe1&W\xb1\xe4\x0f\
-\xb0\x0d\xc7\xdb\xd4\x1c\x8et_\xb6\xe1(\xbf`\x09\xe6\
-\xcc\xb7\xa1\x83\xf5\xbeJ0g\xdf\xaf\x04\xbb\xe3[2\
-3`\xfe*o\xc9|\xfb\xfd>\xa6\x1d\x12\xfc\x9b\xc5\
-m\x15\xfcNvr}\x88\xd9\xb2\xfe\xea\xe6&\x0f\xa6\
-\x1fM\xc2\xd6\x86\xeaH\x8dA\xfcb\xdd\x87\xb3\xa9\x86\
-\xde\x91\x8aq\x8d\x8a\xfe\xe9]l\xf8\x10N,\x9b;\
-\x14\x04\xf6\x07\xd5\xafz\x9aqP\xfd\x10\xd6Z\xb0\xd5\
-c?\xfd\xe7;\x8a\xde\x9e\x80\xfcf1\x15x\xee\xb1\
-\xdfdJc\x99\x96\xabK\x87\x0elm\x8b\x1b\xd6\x94\
-u\xda\x147\xa5\xb7\xcd\xeeH\x0d\x84e=\x02\xf7\xd1\
-C\xb7T\xe0Y\xef\xc3t\xd6\xfbj\xdfQ\xef\xf1\x9a\
-\xde\xf3\x1d\xf4\xde\x1b%O\x9d\xd7j\xdeU\xdf\xe9\x9a\
-\xbe\xeb>}\xb7\xad\xbe\xcb\xd4\xf7h\x00@S\xe7m\
-h\x8fQ\xfd\xc5\xea\xe4\xed\xe7\x9fM{\xa2\x1d\xbfY\
-\xbd\xf8\xfe\xd5\xea\x87\x99\x22?\xbc\xfa\xf6\xc5\xe9\x0f\x0f\
-7_e\xb7\xf0Ow\x1d\xdf|\x81\x1d\x88.E\xfc\
-\xe3\x97\xab\xbe\x01S\x7f\xcf\x7f}\xba\xe3\xd0:\xd3\x0c\
-\x9fx\x8c\xea\xbe{\xd5\xb7f;}\xbd\xea\xf1\xe0\xf9\
-\xaa$n\x0e\x9c\xf5K\xec\xc0O\x9f\xfd\xb3\x13\xbd\xeb\
-\xc8\xb3\xd3\xb3\x17\xab\xb3\x8b+\xe0\x0c\x1e\xb2^\x1f\x07\
-\xab\xbf\x91\xbc\xea\xcf\x06\x1e\x8b\x83\x8b\xb1yv\xdc\xb9\
-\x9eu\xfe\xe7\xd3\xd3\x1e\xaa\xa8\xa9\x12!\xeb\xfc\xe0\xf8\
-=\xe7\xa6$pe=\xb7V\xdb\xa9\xa9G\x04\xed\xa4\
-\xb2b\x98\xc1\xceC?\x95X\xbf$\xf0\xdd\xd9Y7\
-\x8f\x87\xaf\x8f\x7fZ\xf5\x9e\x7f]O\xdf\xac\x13\xdd?\
-N\x7f(\xda*k\xbc[MPQ\xfcv\x02'Y\
-\xc3\x91\x87\xcf\x9e\x9d\xfe\xb8>\xba\x95\xeaK\xd0T_\
-\xfc\xb9\xb7\x9e\x9e\x9d\xbe\xf9\xdb\xd9\x0a\xc4\xfe\xbe:?\
-\x7f\xf5\xed\xcb2\xf3\xfa\x1b\xf3\xea\x8f?\xd5)kh\
-L\xf8\x95\x8b\xbf\xfbq\x06\xfd\xb4\x0d\xbd\xedc\xd5\xe5\
-\x14{\xcd\xcc\xfc\xf2\x81\x9f\xae\x1c\xd8\x8c\x17\x00\xf4\xfa\
-w\x0d\xae\xde|\xb7\x13\xdf26\x9a\xde\xba\x85\xca\x84\
-\xae\xaf\xd8/\xb8\xc1\xbe=~\xf6z5qY\x7f\xdf\
-\xbfz\xfb\xaa\x83#6:\xd5Uw\xfa\xfc\x93\xcf\xde\
-\xac\xce\x8f_\x1c\x9f\x1fo\xcci\xd3\xee3\xebO\xfe\
-\xe3\xb3o\xdf>~{\xf2C\x7f5\xbe|\xfd\xea\xf9\
-\xeam\xb5\xa6\xe6\xe0\xbe\x9b\xc2\xb0H\x9c\xff\xbc\x84b\
-\xfa\x11\xc9\xd4\x1c\xce\xfc\xcb\x97\x1d`4\x17&\x96\xea\
-b]\xe6\xd1t\x9d\xed\xab\xfe\xfd\xf4\xdd\xd9\xf3\xd5\x17\
-\xb5\xa3\xda\xdb[\x5c\xb2\x5c\xab;\xe2\x9b\xafW'\xe7\
-\xff3\x0c\xeeD\xcc\xfa2\xa7\xdf\x9d\xf7\xdd\xd9~\x1e\
-~\x1a\xb16\x9aI\xd5\xf3\xe3\xb3\x97\xab\xf3\x0d\x5c\xf2\
-\xe6H\xe92v\xa3\xffRb\xf5\xb4\xd7\x0a\xc7\xbd+\
-\x7f\xfb\xef?\x93\x8c\x8dbs-k\x0b\xad\x13\xaaj\
-\xea#\xda\x1d\xa5\xfb\xdf4\x82\xe3/.jPO\x8e\
-_\xaf=\xbc\x9fq\xbe:Z\x9b\xcf\xd3\xe1o\xc0\xbf\
-=\xfd\xebtdz\x7fw\x9e\xdas\xee\xf8\xf9$e\
-\xe8E\xb1<\xefG\xc1\x05N\x8a\xdeB\x7f\xb44g\
-%\xc5\xdf\x22\x11\x05/[I\x1d\x1b=\xe5\xb3\xb3\x17\
-'\x8f\xff\xf7\xcb\xa7}s\xd0\xe7\x8f\xff\xef\xf4\xec_\
-\xa3;\x16z\xfc\xec\xf4]'\xa3G\xae\x17\xcf\x1f\x9f\
-\x8c\xd7{\xf5\xa6G\xf6Go\xbf\x7f\xf9\x9f?\xbey\
-\xfd\xd9\xa3\xe9@\xbd\xa7\x82U\x9d\xbe\x11p\xb6z[\
-n\xd0\x9d\xfa\x1f\xe7\xe7\xdf=~\xf4\xe8\xbb\xbe\xa9a\
-;={\xd9\xcf\xeb\xff\xbdyU'<\xfa\xfby\xdf\
-\xd0\xf0/%wt\xfbu?\xfa\xab\x8b\xae=\xdax\
-\xf9\xe7\x9f|\xb2\xab\xd8\xee4M3\x85=\x8a\xd7\x9b\
-\xab\x82\x22~\x9c\x0f\xceg\x95\x8b\xabw\xec\xb5\x91\xd9\
-4s\xc4\xaa,w\x0ai\x80\x8aK\x920\x8c#\xe7\
-\x92bY\x92j,IR\x8d\xa3'1\x93D\xb0,\
-)M\x96$E>yz\xc4sI\xb4(\x09Y}\
-I\xd2\x17|t\xf4\xe5\xa5>\xc9\xb2$\xe7EI_\
-\xcaW\xf4U\xce%-3N\x90\xb9$\xe9\xab\xa3\xa7\
-\xf4T\xe7\x92bY\x92\xb0.Iz\xdaiz:\xb7\
-\x02\x86eIN\xb4(i\xf8\x9bK\x1a\x19\xbf\xfc\x99\
-\xf8M\x8e\x11\xfb9F\xde\xb5g(\xab\xaa\xcf5\xf1\
-\x91\x93\xddc\x8el\x86\xa2\xcb\xb6\x18O\xe2h&O\
-p#oa\xec)\xcd\x22\x96\xc7\xbf\x1es\x89\xbe<\
-jl\xbc<\xfe\xf5\x98K\xca\xbb\x1a\x7f\xc5EI\xca\
-\xb8O\x9f\x94\x17%\x85\xc8\xb2\x9f|q\x85'\xd5\x05\
-I\xb8(\x04\xbb\xdb\xf2\x5c\x88/\x19\xf6\xc2\x92\xe1\xb4\
-np\xff\x8b\x03;i\xdac\xd4\x8c\xf6e\xa8\xdcE\
-y.D\xd6\x0c\xcd\xf8\xb8\x91!\x84o\xeem\xdd\xe1\
-\xc3\x89\xc9; \xc6\xf1v\xc4\xe07\xf7\xb4\xa4\xf1\xc1\
-\xb4\xb8\xedK\x0b'\x7f\xc11\x17\x12\xb7\xa3\x85\xbe\xb9\
-\xb7\xd5\x92\xfd\x88\x89\x88'1O\xc9\xb1\x1c\xb4\x00\x9c\
-\x16%\x1d\xc5W\x91sI\xba,\xc9|1\x90>\xf9\
-\xe2\xc9\xd1\x93K\x92\xfc\x9a\x22H|I\xd2\x91\x1e\xd9\
-\x91\xcc%]\x93&\x10u\xb1\x08\xfa\xa2?\xe6\xc1=\
-\x97\xd3\x04\x03\xd2rp\xaf\xc7\x5c\xd22\xe3\x82\xd7\xa4\
-.\xef\x0f\x9bKZf\x5c\xcdsQ\xd2\x97\xf5\x98K\
-Zb\x1c\xf7\xf0\xb1\xcc\x05\xf7\xf8\xec\xe5\xe6m/\xd9\
-\xd6\xa1\xe6|\xe7\xdd\xb2\xb31\xb3\x1f\x22\xd7\xb3\x02?\
-\xe8s\xa5\xfa\xb9\xff8\x09\x9a\xd6/N_\xacj\xaa\
-\xd3\xbd\xe8\xf9\xfao\xb3\xbc\xb3\xebf\xc2\x82f\xe6\x9b\
-\xbd\x00X\x1d\x85\x0f\x1fR\xb6\xd4`\xee/\x11\x1b\x13\
-*\xcb\x83\xf9gP\x17\x9f5\xb5H\x8d\x04\xa1a\xb3\
-\xf8\xcdo\xde/\xef\x1c?\xceT\xc7\x8d\xe6\xc7\xd7\xec\
-N\x1b\x99]\xfd\xbf\x1e0\xb7\x08\x00\xe3C\xb4\x06\x9e\
-\x10x\xf0\xf56\x8a\xd9\xc0\x22\xbc\xa3\xc1M\xd36P\
-z\xdf\x15?\xa4!mA\x05\x18k\x07\xa2\x85z\x01\
-\xda\x00\x87\xb6\x04e\x89A\xf4\x96,\x87a\xcd\xd1\x81\
-\xa5\xcb\xa9\xe3\x0cY\x18\x0b\x15V\x88\xd2ah\x0b\xe6\
-9\xc0P@I\xaa\xb3\x0c\x91\x16:?\xa1\xe3\xfe\xfb\
-\xd3\xed\x1a\xd8\x86\x92f\x1a\xcc\xd9!\x9f1Tc\x01\
-\xc0XV\xa0 \xccV\x17\x99P\xc2f\xc2F\x1d-\
-:\x9cF\xc85ld\xc8a\x1b\x0a-W\x1f\x001\
-\x19\x81\x8e\x0f\x00DFIG\x82\xc6E\x91\xb7QJ\
-\x01\x0eY\x00!\x0e\xcd\xa4\x81A\xa0\xad\x16z\xf5b\
-l#\xeaB\x8f'\xf4\xe7%\xf3b\xb0\x90d\xd9\xd7\
-\xbcH\xf0\xc1\x9dY\xfe\xcd>\xb6\x19\xc1\x99\x16\xd5\xcb\
-\xb1SW?K\x9c\xd9\xbe6.\x92\xeb\x09\xa33\xb4\
-n#b\xcb\xcc\x01\xf1\xa2\x14\x80:H\x07G\xd5v\
-\xb8hk\xd4`\x8c-l\x1eY\x88\x86m\x10\xc12\
-\xd2\xa4\x96(\x87\x1e\xeb\xa1Ln\x14YmB\xac\x96\
-\x1e\xfa v\xf3z\x1c\xc5j1\x8b^\xe9\xde\xd8\xde\
-e\xd1y\x1f\x94\xf9\x83K\xc3\xbe\xcd`V\xce\xc7C\
-o\x99\xa1e\xecM\x0c\x82d\x83D\x03Bv?\x8c\
-&\x9c\xcc\xb6\x8dd\xc0\xa8\x98\xb7t\x01\x8aC\x84h\
-\xacH\xd1E\xcd\xc0H&-\xe9\xc8\xe2\xda\xa1,.\
-\xb0#\xa9\xe4\x8a\x13\xd2i\xb2\x16\x89.>\x802\x8e\
-\x1aJ\xa2\xe2\x88T[\x00\xcdhhSjra\x1a\
-\xec \x03\x86H\xcc5\x06P\x97$\xae\xd1\x82(\x13\
-(H\xd3\xc8j\xa0\x22\xdd\x0a\xf0j\x98(9_4\
-9\x1cTG\x19f\x1e\x87\xd9\x00%\x8b\xa5\x0d\x12M\
-\x9c\xd3\xa2\x00\xa2d\xee\x080#\xd3Ad\xb3H\x88\
-\x09\xf9zN\xf6\x8e\xf17\xbcq\xfc'\x13\xa8\x0c8\
-\xe5@\xe3\xc9io\xfc\xf0|\xb2\x9f\x9d\xa1\x03\xff4\
-Y\xd3\xd5\x0fz\xd7\x0e8\xef\xb8\x168Y\xd5\xe8;\
-\x95 \xd6.S\x80\xd9\x000[y\x06\xb0\x1e&\x1f\
-D\xa7\x95\xcb\xee*m\xd8\x80t\x5c\xaa1\xbc\xb2:\
-V\xf6\x82u\xc6\x81G\x9d_\xd9\xaa\xc4USch\
-\x8e\xaez\xf9\xc2?\x17S\xb7W6p\xa7\xb2\xfe\x1e\
-\xcazd\x01C\x80\xf0&\x22]\xa9AY\x19\x95\x8d\
-\xf4\x01\xb1RvH\xbb6*[\xea\x80\xd6\x19\xa5\x1d\
-!\x96v%\xee\xde\x95\xe5\x9d\xca\xe6\xfb\x8c\xacr\x01\
-,^\xca\x06t\xd5tPVK\xa1R\x7fD\xea\xff\
-\xd5\xa8W\xa3\xb2^\xca\xd6\x19\xc3\xc8\x8a\x8f#\xab|\
-{e\xafQPw)\xe88S\xf0\xf2\x85\x8f.\x03\
-\x09m\x0c_z\x90\x1d\x05)\x0e\x00;\x98\xd1\xf4R\
-7w\xd05\x072\x1a\x0dV\x1e\xb1f\xa4\xc2\xa5u\
-\xf0\xf2u\xe7\xcd\xb5\xfe\x8f^.\xa4\x82\x1e8\xbe]\
-\x8da\xff\xec\xdd\xeb\xd5\xe3\xd5\xf7\xab\x1eJ\xea\x9b\x92\
-g\xa7\xffZm\xca\x81us\xfc8\xfb1~\xf7\xe3\
-\x06(\xea\xfa\xc7\xa0\x8f\x9f\xbd;?\xdf\xc6\xfe\xd9o\
-\xc4\xd4o\xc8\xd4\xeb\x845\xba;\xa9\xb8\x1d\x12TZ\
-\xa8\xe7\x1dq\xcey\xde\xfb\xad\x83d5#\xbd\xb06\
-\x90\xc3\x8a\x9c\xeb\x17%\xea\x17\xd7\xb3&$\xf3\xf9\x87\
-\xe0\xa7;\xcbP'\xfb=\x16\xea\xf70\xe1\xf9\x05+\
-\xd2\xfb\x9f \xdeG\x05\xff\xfb\x9a\xfeL>g\x9a\xb2\
-\xf7\x08N\x1e+\xbf\xad\xc9\xc5\xcd\xd3\x03\x22\xd7\x07{\
-\x10v\x953\xcd?\xa6\x13\x1f\xc3tb\x8f\x11\xa7\xcb\
-\x13\xc2q>\xf1\xfe\xf3\x87\xad\x99\x07\xa6\xf3\x94\xfd>\
-\xc2\x19\xc0\x95\x92\xcf\xac\xa0}\xebc\xa2\xc4\xc9Q>\
-\xaa\xfa\x7fYU\xe7[\xaa\xca\x93\xaa\x1fU\xf5\xbf\xac\
-j\xc0-U\xd5I\xd5_\xe1<`\x07\x13\x08x\x99\
-\x8ak\xd4\xf7\x07\xf3R|\x9e\x0fYn\xaa\xeb?\x86\
-r\xbcz?\x15\xe4\xc37C/\xfdp\xe2\xf5\xf1\xb3\
-\xd5\xeb\xf5\xb7E\x0f\xea\xd8\xae\xaa\xad\x06\x14\xfa\xa3\x9e\
-\xd5\x1a\x90\x99\xc5\xe1\x10\xf9\xa2\xccd\xbc\xc6\xceS\x1f\
-\xe2pf\x15E\x95\xa6\xaa\xc0\xc3\xe6(&!\xab\x87\
-4\x8b\x98\xc4\xa27\xad\xc0$(\x19\x13\xfeiN\xea\
-\xa4\xf65\x1c\x1b\xc7e\x8eK\xa2if\xda{1}\
-V_\x89\xdc\xa0\x03\xed\xaf_\xf5\xa7\xc7\xb2\xc1^\x1c\
-\xf7\xef8\x9f\x9d\x1d\xff4vmD\xaf/J\x95\xf9\
-\xc1|\xa2\x1d\xde\xa2R7\x8dQ\x02 %\xbd7\x8b\
-\xf8d\xad \x89n\x08X\x183\xa7\x8f\x95f5\xc3\
-+\x91R\xb5\x0c\xaa\xd5e\x904U@\x91C\xc3f\
-,H\xd9\xb1C\xa3\xe6!`<4\xcaK\xebY\x1b\
-\xa1&\xfb\xf6Y\xd6\xd84\x1c\xd7r\xbd!\xd5U\xc6\
-k\xa2Vs\x8c\xdd\x0e\x90\xc6\x1d\xb3\x06aX)\xac\
-1\x93\xb0\x0f\x18\x19\x90V\xc2v\x0c\x1fO\xd5\xe4\xa1\
-\x8f\x89\x0dA\xc1\xea\x8d\xd2\x92\x9c\xb4\x0a@\x000\xa1\
-\x8eqS\x0e\xc2\x83\xacFO\xf1\xf5\xa2\x84#\xe9\xd8\
-\xf0h`c\xa3\xcc\xd4m\xad|d\xa3L\xe8=p\
-m\x89\x1c^\x9c\x9a\xb9\x1a\x17\xe4\x9c\xae\x5c\x9d\xa7@\
-\x1aN\x14\xb0Q\xa1\xca?\x92\xccU\xa4\x80\x93K\xd4\
-\xf433Y\xa3.(\x86^YY\x0e\x03\x8a\x8f\xaa\
-\x88\xb8\xd98\x06,\xa3\xa0\xa2v8F\xb2\xeen5\
-\xa4\x0a\xb7I\x98\xe4\xa0\x1f\xf8\xc5E\xedPa\xe0a\
-\xbbs\x8a\x1b\xc2.\x948T\x1ax\x15\x9bT-l\
-\xe4\xbf\xb8\xa8\xb3.h\x92l\xc9\xbenp6\x8bM\
-\x03+\xb8\x8e\x94\x0b\xfaa\x8d\x02:*\xd5\xd0\xa8\x86\
-Q\xc7\xb8\x85G\x10\x96\x5c\x1fz\x84<\x8e~!\x00\
-\x1c\xce\x83Q:b\xd4\xe0\xa7:S\x87\xb0\xa5\x199\
-O\xb6\xbd#>\xb3b^\x80\xe77\xecT\xda[\xe8\
-\xec\x5c\xbb@4\xc8\xf1\xc0Cl\x88\x18H\xf2`\xdf\
-U0b\x83\x07\xef\x1dK\xca\xbb\xe7\x81$\xfe-\x01\
-d\x1e(\xcai\x1c$\xab\xd0\x0dI\x95u\xd5\x8d\x8a\
-\xeaC\x89\x8c\x9c\x96X\x96Qo\xd4\xc2X\xc41u\
-pM\x17\xc5\x0e%\xb9\x12\x0c\xb3S/\x87 \x1c\xa2\
-\x87\xb1\x00\x96\x9d\xa25#\xc8\xa4\x0a \xa2\xe8f\x07\
-\xa8c8\xe7\x8a#\x96 9\xc3\xa4\xec\x88\xc9\xeb\xec\
-\x09\xd5\xc6\x08\xa8\xbe-\xd1\x9aJD\xf8\xf6\xb5\xbd\xb1\
-\x99q\xb7\xcc\xa9\x9b\x95\xd7\xc1\x19ytj\x8f\xe0\xae\
-9\x124\x84\x90(2\x1c\x12p\x80(\x0dd\x98\x09\
-\x0a\x82\xf2p\xb2z@\x14AR\xfdA(4Z\x18\
-T%ZY\xcd\x85\xb4cuq\x8e5ff>\x04\
-*\x07\x8b\x9a\xe7Lh\x11\x9d@`34\xb4\x09\x02\
-H\xccP\xcf\x86\x12\x8e:C%Z(&\xe7\x1c\xad\
-X\xc7\xe84C\xd9[ \x88\xf8\x0cEj\x94\x1c\x94\
-\xa5W\xa1\x8eF\xe5nH\xce\xa6\x07\xa9M\xc1A\xab\
-fj\x00\x11\xaa\xa5\xbf&F\xd5\x9a\x0d\xd3\x82T\xc7\
-\xd9\xb5\xb99Z\x99H\x88\xa7QE\x03\x111+\xfb\
-\x920\xa2\xdcer?\x1f\xf4\x94e\xcd`\x88\x11\x84\
-\x8d$\x08J\xa7\x09\x15j\xa2\x9aZ:Y\xb4\x94\x0c\
-\x90-\xf4\xa8PJ7\xd0-\xd4\xbc\xb9\x93J\x14\x96\
-l\x9c<a3\x9e&t\xe2\xffh\x1b\xb5\x06`A\
-1]\xa70\x09'\xf3\xa9G\x13\xb6\xdd\xfb\x05\x14\xc1\
-\x9a+\xc9\x90p\xa3\x11\x09\x0aN\x96\xdf\xd1\x1d\xac\xec\
-\x0a{\x0ct\xf3\x07\x14\xa1\x7fZ\x9c\xb8\xb2\xd1\xc7\x1c\
-\xba\x96k\x1f\x0b\xbc\xa1\xf6qM\xcd\xb1\xf6\x910/\
-J\x19\x08T\x0a\xa3\xe0\x94\xa8:\xc4\xd4H\x0br]\
-\xf3NZ\xa4{\xac3_%\xa0A\xb0\xd6\xa1\xb1\xe0\
-\x88\x80\xd4\xa8Lg\x1e\x1eY\x98X\x92\x0e\x99\xce\x93\
-P\xf3\x9e2\x9d)\xdec\xa6[.\x9a\x97\x93\x9f\xfc\
-\x91\xfc\xfeH~\xf7\x93\xfcB\x9aY\x8a\xddmF,\
-\xb4\x83\x98\x7f\xe4\xc9\xdfI\x9eT\xc8Y\xac\x9bG\xd4\
-\xd0y|p\x9c2\x89\xe3\xd5L\xe2p5\x93X^\
-\xca$\x0e\x973\x89\xeb\xe5L\xe2z5\x93\xb8^\xcd\
-$.\x973\x89\xcb\x95L\xe2t5\x938N\x99d\
-1\xc6\xf3\xb5\xf5\x81\xfd\xea\xeb\x83\x9b\xf3\xe4\xc3)Q\
-\x226\xb7NX\xb4\x08\x17\xbe\xcdd0\xf6c\xec.\
-W\xee\xe6vL\xd2d\xed-\xe0\x5c\xde\xe2\xcdj\xf1\
-\xcdE\x98\xf3\x02\x80h\xa4\x1a\xd8\x81\xf9);\x5c\x89\
-\x98\xb8\xc0k?\xd5\xba\xb9\xae\xb8\xce\xe4\x1c~\x9d&\
-\xe7D\x97JR\xc468\xbc\xe6f\x19e{q-\
-\xd6\xabd\xb8\xb5\xb8\x16\xd3\xdaZ\xbd\x96\xe1m0\xfc\
-\xe1\xb4\xc26\x9d\xba\xb5\xc2\x96\xdb\x0bl\xc8\x0d\x18\xa9\
-#\x17\xab6\xa8\x0d\xd5\x0a\xbaXM\x9a\xf7oW\xe0\
-t\xd0\x0f\xf1\xa2l\x16B\xcb^t\x8d\x11\xd0G\xe2\
-EW\x9cf\xc9\xad&/\x1a\x01\x8f\xe6\x8a\x19\xba\x01\
-$+C\xa1\xd1\x15O\xdc\xc5<\x13,\xb1\xb6\xf7\x02\
-\xf6\xee@\xe5\xf2\xe0\x12k\x0dP\x9c\xd5o\xc9\xde\xb2\
-kp\xda\xdc5\xc6e\xc3N\x04\xd3\xb0\xca9_\x9d\
-\x85\xf1Yb}\xc0v\x12\x94\xf2\xde\xa6\x09-\x093\
-E\x0f\x1ffc\xb0\xb1\x0al\x99$d\xf9`\xb96\
-\x88\xd4\x0f\xec\xf7=\x8d\x9d\xfd\x02c\xf7\xe1D\xef\x9f\
-M=>\xe68\xa0\x8c\xa5\xa0qKd\xe3Z\x07g\
-lL\xaa4L\xe6\xdcI\x9c\x0e8\x9aJ\xa8\xc7\x10\
-\xc29\x01\xe4@\xac\x81&\x9a_`\x5c\xf546u\
-\x0a\xc2-\xd4\xb5%\x03a\xd6u\xc80C\xc7\xceh\
-\xb6\x84p\xe2\xdbE\x98H\xdf\x7f<\x02>\xda\xf1\x08\
-k\x8e\x95\xf3\x08\x1a{\x10nc\x12-D|\xf6>\
-\xcd\xc6\x86\x149\x96\xe2\x04L0\xce\xcb-\xc0\xa3\x06\
-\x03\x0c\xd5\x86\x94i\xa0\xc8| \xda\xdc(\xd1'\xac\
-\x06=\x9a1\x0a\xdb\x16\xca\x95\x03\xd3u\xc00\xc8{\
-'I\x1bE\xb8\xd6Hr*\xa0\xfc\x06\xec(\x11n\
-aG\xf4+\xf3k\x8a\x86D\x198\xd4O\xe6F^\
-c\x14\xe2$Y\x98eP\xd8\x01k3u-$\x89\
-\xc0\xa2\xc6\x92\x1aZ\x8ax\xa1\x0eb\xce%\xcf\x15(\
-\x0bbM\xe22\x0e\x19B\xe9p]!\x91X\xcfp\
-=\x0a\xa52\xc7t\xd0\x19*\xd9\x1c\x125g\xa8A\
-\x03%\x8f\xa1\x80\xa4&\x0cY\x86\x88\x8d\x9cI\xf1\xc0\
-\xbd\x05a \x95\xb93\xa1\x99\x0c\x13HL\x1c\xf5F\
-V\x87\xf5W4P!M\xea\x9d\x14\x0a)\x07\xc1\x0d\
-B\x98b\x90\x08FA{[R-j\xec&\xcb\x9b\
-\x00)p\x99.\x90\xa8\xea\x01gC'\xd5,\x0c\x01\
-\x1d\xfd@\xa0\xa5:\x04\x0f\x98\x92\xda\xe06\xd9\xea(\
-E\xa1\x90\x0eI\xa3DT\xb2\x01cp\xd3\xabW\xde\
-m\xd9y\xf1]\x88\xfaW7\x80\xf9\xfc\xff\x01\x9e\xd5\
-\xc9%:P\x01\x00\
-\x00\x00\x18\xd8\
-\x1f\
-\x8b\x08\x00\x00\x00\x00\x00\x00\x00\xed}ms\xdbH\x92\
-\xe6\xf7\xf9\x15:\xcf\x97q\x1c\x99\xca\xf7\xccrw\xcf\
-F\xdcL\xec\xc6F\xf4\xc6]\xec\xce\xc4~\xdc\x90%\
-\xda\xd6\x8e,)$\xb9m\xf7\xaf\xbf*\x90\x12X\x14\
--\xd0l\xda\xdd;c\xb8\xdd&\x1eTUV=\xf9\
-\x06\x14\x80\xc2\xf7\xff\xf4\xe1\xed\xc5\xd1O\x8b\x9b\xdb\xf3\
-\xab\xcb\x1f\x9e\x11\xe0\xb3\xa3\xc5\xe5\xe9\xd5\xd9\xf9\xe5\xeb\
-\x1f\x9e\xfd\xf5/\xff<\xcfgG\xb7w'\x97g'\
-\x17W\x97\x8b\x1f\x9e]^=\xfb\xa7?\xfe\xee\xfb\xff\
-5\x9f\x1f\xfd\xe9fqr\xb78;z\x7f~\xf7\xe6\
-\xe8_/\xffv{zr\xbd8\xfa\xc3\x9b\xbb\xbb\xeb\
-\x17\xc7\xc7\xef\xdf\xbf\x87\xf3\x15\x08W7\xaf\x8f\x9f\x1f\
-\xcd\xe7\xb5\xe6\xedO\xaf\x7fwttT\xc5^\xde\xbe\
-8;\xfd\xe1\xd9\xaa\xfc\xf5\xbb\x9b\x8b\xa1\xdc\xd9\xe9\xf1\
-\xe2b\xf1vqyw{L@\xc7\xcf\xc6\xe2\xa7c\
-\xf1\xd3&\xfc\xfc\xa7\xc5\xe9\xd5\xdb\xb7W\x97\xb7C\xcd\
-\xcb\xdb\xdf\xaf\x15\xbe9{UK\x8f\x9dy/C!\
-*\xa5\x1c#\x1f3\xcfk\x89\xf9\xed\xc7\xcb\xbb\x93\x0f\
-\xf3\xbej\xed\xe3\xb6\xaa\x8c\x88\xc7\xf5\xd8Xr\xb7R\
-/>\x5cT&>\xd9\x99\xe1\xe8\xba\xf4\xca\xfeu\xfd\
-\xfbP\xe1\x1e\x80\xdb\xabw7\xa7\x8bW\xb5\xe6\x02.\
-\x17w\xc7\x7f\xfe\xcb\x9f\x1f\x0e\xce\x11\xce\xee\xce\xd6\x9a\
-\xb9'\xbf\x93\xdbi\xe4\xf2\xe4\xed\xe2\xf6\xfa\xe4tq\
-{|\x8f\xb7\xfa\x9d94\xa0j\xf8\xec\xeeM\xdd\xe5\
-\x1cv\xdf,\xce_\xbf\xb9\x1b\xf7\xcf\xcf~xV\x07\
-\xcc\x8e>\xec\xdfw\xe9\xc5CC\x08\xc2\xc3\xa1{9\
-\xeb\x87t\xa3\xd6\xd9\xd5i\xedY\xed\xf7\xeb\xab\xf9\xe5\
-\xe2\xc3\xdd\xfc\xa7\xf3\xc5{\xa8\x02~\xee\x9b\xb8zw\
-w\xfd\xee\xee\xbfj\x89\xc5\xe5\xb2\xad:\xa8q\x84\xcb\
-\xc3C\xbd\x07\xb0o`\xf1\xe1\xfa\xea\xe6n\xfe\xea\xfc\
-b\xb1\x14x\xfc\xe6\xea\xed\xe2\xf8\xfa\xfc\xb2\x8e\xf0\xe6\
-\xaa\xfe8\xbd=\xbe\xfa\xf0\xf1\xf5\xe2\xf2\xb8\xd6\xb88\
-yy\xb18>9\xbd\xab\xb2n\x8fk\xe7.Nn\
-W\x9d\xbb\xbe|\xbd\xb5\xe9\x0fg\xd7U\x8d\x1e`[\
-\x0f\x7f\x1c\x0f\xff\xb1\x1e\xff\xfe\xed\xe2\xee\xe4\xec\xe4\xee\
-\xa4\xfe^\xd1z\x8fh\x0c%j\x99j\xb4/\xfe\xfd\
-\xcf\xff\xdc\xf6\x86\xfd\xd3\xd3\x17\xffyu\xf3\xb7\xba\xbb\
-\xdaZ\x81\x93\x97W\xef\xaa\x82\x86:\xabrg\xa7/\
-\xaa\xe1\xbc=\xb9\xfb\xe3\xf9\xdb\x93\xd7\x8bf\xa1\xff\xbb\
-\x1a\xca\xf7\xc7\xe3\x81\xae\xf0\xdd\xc7\xebE\x05\xbafo\
-\x16K\x0b\xdc\xe6\xb4\xf5\xbf\xb7\xe7\xad\xd2\xf1\x7f\xdc\x9d\
-_\x5c\xfck\x13\xf2\xec\xe8\xf8\xa1\x9f\xc7\xab\x8e6\xa0\
-\xed\x8e\xe3\xa8;\xf7\xc3\x1c\xf6\x1e\x0c\xa1)\xe5\xac\xf1\
-[\xd1u\xf6\xde\x9f_\x9e]\xbd\x9f\xdf\x9b\xa1G>\
-\xdb^\xe2\xden)c\xb3\xc4u\xed\xde\xed\x9b\x93Z\
-\xea\x87g\xbc\xed\xe0Uu\x8c:\x9ef\xa0\xb8:\xfe\
-\xfa\xdd\xf9\xd9\xe2\xee\xeabqsr\xd9H\xa0\xb5C\
-7U\xd4\xd6#W/\xff{qz\xb7\xfd\xd8\xcb\xab\
-\x9b\xb3\xc5\xcd\x83$\xda8pzuqu\xf3\xc3\xb3\
-\xdf\xfb\xb0\xad\x0e\xb5\xbe\xdd\x1fx5l\xcfFsy\
-yr\xbbX\xed\xde\xbe\xb9z_{U\xc1\xbb\x9bw\
-\x8b\xcd\x11\xfe|u\xf5\xb6\x0d\xcd%\xd1D\x1e\x11x\
-\xfa\xe1\x87gs\x12\x04\x16\x11\x7ft\xb4u\x96\x02\x9c\
-)?\xc5}m\x00?q\xa8\xd6V{\xdc\xe8\xbb\x9b\
-\x9b\x1a\xf4\xabW}\x5c\xdc\x8c\x01e\x1c\xcd@\xff\xed\
-\xf6\xf1\x0c\xc7\xe6/_^}X\x1d_\x99\xd9X\xa0\
-Rqo\xce\xd5J\xab\x1e\xaa_W\xac5\xf4@_\
-\x03X\x99\x1f\xc0\x9f\xceo\xcf\xab\xd3\x8f2\x87\xadF\
-\x8b\x0a\x9em\xa0-\x92\xd6\xacY;\xc0\xe0u\xbb\xfe\
-\xb0y\xe8\xe3\x96C\x8b\xb7\xd7\xab\xa3\xf5`\xf3\x97\xc1\
-\x1f\x1e\xbb\xc0\x80\x9f-^\xdd\x8e\xcan{\xec\x98\x8f\
-\xc6z]c\xeb\xf5\xe2\xb4\xa5\xc7\x95\x981\xb8.\xc7\
-\xde\x17\x95\x91\x851>_\xffW\xd3\xe0\xd1\x8b#\xd7\
-\xfa?\xdaZ\xe2\xe3\xb2\x04!\xb6\x7fpk\x99\x9f\x87\
-<\xb1\xad\x9d\xbe\x0b\xf3\xab\x9b\xf3\xd7\xe75\x88\x0f\xe5\
-tI\x94G\xab\xd3\xe9hmpZ\x1e\x02\xcc\xf75\
-\x87.Nn\xfe\xe5\xe6\xe4\xec\xbc\x1a\xd1z\x85\xfe\x88\
-h\xad\xf5\x10\x94n\xef\xae\xae\xdb\xef\xb1tCDm\
-\x94Y\xb9\xbb\xfbx\xb1X\x1e\x99\x0f\x9e\xf7\xe2\xf78\
-l\xdf\x0d\xd0\xca}_\xacW\xb9z\xf5\xeavQC\
-\x13\xd6\x0eN\x0b\x93\xcf\x17\x86[\x84\xd1\xc8\xc6q?\
-\xe8\xcf\xe5\x88\x8bMr\xc4%\x9e\xee\xb6\xcb\xa2\xbcz\
-u\x18\x8e\xb8\x94'\x85\xad\x22\xe1W\xe5\xc8q\x9a#\
-\xe7\xe9n\x1f\x8c#\xd7\xdf\x1cG\xc8\xd3\x1c\xe1D\xb7\
-O\xcf\x16\x8b\x83q\x84\xfe\xb4\xb03^\xe4\xe2\xec\x00\
-\x1c\xd5\x93\xca\xbb\xc5\xcd}\xc5\x96M\x11\x08\x19\x03\xc9\
-V\xed\x8d\xe7\xd5\xc0\xa8\xa48\xc6\x81\x8f\xcb\xf2\xac\xc5\
-\x1c\xe9\x1e\x1eO\xbc\x81\xb5\x14\xe2.0.%\x8ad\
-\x8c\x9c\xbfZ\xfc\xcb\xc9\xbb\xdb\xdb\xf3\x93\xcb\xffs\xf1\
-\xae\xf5f3\xf8\xd6a_,Nk\x93'\x17\xefO\
->\xdev\xe4\x9c\xfdy\xf1\xd3\xf9\xc9\xdd\xfdY\xba\xba\
-\x99\x17z\xd6\xd3\xdaK\x10\xc9\xb2\xc6\xcc\xb2K\x9fi\
-5\xae\xa8SV\xe3:\xa5HLZ\xc8\xe2 V\xe3\
-\x8a\xf9\xb40\xc98[\xe8\x84\xb0Cz\x96\xd7m\x92\
-#\xf7\x89n\x97W\xa7\xf4\x8a\x0f\xe3Y\x94\x13Y\xcc\
-2\x16/m\x07a`;\xa8\xc4\x03'\xf4\x7f\xca'\
-\x96\xdb\xc4\xed\xef\xc8\xbd\xa3\xd1\x17r4scK\xce\
-\xa7\x1d\x8d>\xd7\xd1>\xd0\x0f\xcfB \x94]r\x0c\
-4\x15%\x03\x117\xd5\x07\xf4\x03\xd7\xb2\x09E\x8aH\
-\x19\xcbV\x94\x1c\xc20lD\x1f\x1b\xa7\xa9\x8c\xca\x19\
-&7^\xbc\xb9Y\xbc\xaa\xd7+\x8fR\xe8X\xee\xf5\
-\x0a\xfc\xeb\xe5\xf9]=\xc7\x7fw\xbb\xb8\xf9\x8f65\
-\xf1\x7f/\xffz\xbb~\x8e]\xa7}\xce\xfemq\xf7\
-\xe6\xaa\x8a\xad\x8d6N+\x0f\xd3#W\x08\xebG\xad\
-\xc0a\x99\x07\x1c5\xef6\xeab_k\xd4\x14\x05(\
-\x98\xb5\x1f9\x0b\x84\xa5\xf7#\xa7\x10(\x89A\xdd\xc8\
-\xdd@5\x89\xf8\xe9\x91\xfb\xc1G\xde\x97\xfaK\xbdv\
-\xbem\xb3\x14\xf5\x9a\xab\xfd\xbc\xa8\x13\x8f\x7f(1\xc3\
-\xe7;\xd1P\x1c\xd4S\xbd\xa3\xc1\x05H\xd2\xc8:\x1a\
-\x8c\x81)\xd5\xb3\xa7\xc1\x81Y\xd5\xe2\x09\x1a\xb8\x98\x1c\
-\xca\xec\xa7i\xa8\xf357\xe7\x1f\xfe@\x80\xec\xc5\xd1\
-gX\xff\x8c{sF`\xd4\x882\x9b\x13\xa4\x98\x87\
-\xf9Nde,\xb9\xeac\xc4\x1cA\x9c4\xd9\xbc\xb7\
-\x1ar\x08J\x8d\xde_\xd0A9\xcc\x9f\xa6\xcb\xff\x0e\
-\xe8\xd2\x80\xe2\xa9\xc1\xbd\x8b\xf9\xd2\xb6\xa2#K\x0b0\
-\xf5e+\x9a6pe\xe5i\xb2\xcand!\x7f\x15\
-\xb2\x0c\xeb&\x8d,/\xa2,\xb4FVk\xf0\xe4b\
-\x93\xac6\xa3DkCosH1Z\xc0Mu\xbd\
-q\xefU+\x9c\x09\x99\x142\xa2\xb5\x0a1\x81*\x93\
-tl\xf5\x22\xb9\xf8N\x9e\xe8u;\x18[\xf3Gt\
-\xa1\x9b\xfb\x8c\x99A\xc2)*Y\x0c\xea^<w\x22\
-kN\x0e\xee\xa9\x99\x1dg\xe9\x90M\x8e\xadS\xe78\
-\x92\xd4U\x1d\xe1\xae\xea\x93\xdc\xe5N\x96\xa6Z\x0e\xc7\
-\x1d\x02[\xbac%\x09\xc1\x98XC\xdb\xcf\xe4PQ\
-\x9d!(\xa1#\xd1\x8c\xd4\x81\xa5\xa8\xcd\x0c\x01\xcd\xac\
-\xd8\x8e9@\xc1\x1e\xa5\xc1\x82\x9e\xd2\x074T`\xeb\
-\x1c\xd4\x80\x9a\x98\xe4'\x1d4h'\x93S\xd4\x03\x9a\
-\xdc`j\x08\xe9\x84\xea2cJ\x88\xe6\x94*N\xe9\
-1\xc9\xcc\x10\xac\xa3\x0f\xe9s\x16\xef\x89B\x1d\x0b\xd0\
-X`:C\xe3l\x9eM]\x9c\xa2\xcfw\x1b\xf6\x13\
-\x1c\x0b\xeeh\x9a\xc8U\x17\x13'\xe0\x13\x1e\xd8\x9cJ\
-\x0c\x948K\xe9\x9c(\x1c\x0a\x9a\x17\xe9=\xae\x00\xc9\
-\x90A:gu}\xd6\xb94\xe3n\xc4\xcd\x89\x0b4\
-)\xd5\x0b\x18,\x8d\x8b\xe8b\xee\xfb\x90\xd8\x8fOp\
-\xb7L\xc2\xa5\xe0g\x90xzq~\xfd\xffN\xee\xde\
-\xac\x0b\xbe\xc7\x04mt\xa8{pk\xbf\xc7\xeb\xa9\xeb\
-\xb1\xad\xf1\xfa\xee\xe1:\xee\xbbz\xbds\xf1\xa2\xde{\
-\xfa\xc3\xef\x1f\x8f\xee\xf9pt>\x16\xbe\xbd\xbb\xb9\xfa\
-\xdb\xe2\xc5e\xbd\x7f\xbd\xfa\xbd\xbc1\xf4\x02\x81$\xb4\
-\xd4\xed\x1eo\x1c\xd4\x81\xbe\xb8\xb9zwy\xb6\x0e\xfe\
-\xf7\xd5\xf9e\x8f\xd6\x9b]\x8b\x9b\x8b\xf3\xfa\xcf\x0b\xbd\
-\xc7\xceN\xea-\xa5\x9b\x9b\x93\x8f\x9d\xb0\x86\x0e\x97\x9b\
-\xb5$\x90\xad\xe0\xadW\xa5\x95\xb5\x7f;\x9a\x13\x13p\
-b\x99q\x013t\xe5\xa3?5\x94\xc1\xdc\x9cg\x9c\
-\xc0n\x85\x1b\xa6\xe0\xe2\x16\x1dfP\x98x\xbd\xfa\x8f\
-\x15\xd6{\x8b2\x06O\xe7\xb2\x84\x83\x81\x0a\xdb\xa6\xb0\
-\x10\xd0\x08\xa1\xf5\x86\xc3\xc04\xd9;\xcc!\x85Kn\
-\x08K\x01C\x97\x9980K\xf1e\xa3\xa9\x10\x85\x90\
-g\x12`\x98\x9e\xd2\x81\x05,\x8asY\xab\xaf\x08\xe9\
-*64\xea\x08\x12\xe86s\xed\x84d\x00\x89\xb9\xf5\
-22A\x19%h\x1d,\x08ZH(\xd6\xaa\x17\x82\
-\x08G\xc9\xd6\xe88\xa0\x92\xa0Z\xc4\xb25;\x8e\xbd\
-\x14\x08Q\xcc5\x8eFl\xa4s\xac\xde\x93\x1f\x06B\
-\x12\xd8\xe0QU\x9d\xb0Q\xabc\xc3\xa3\xf6Gl\xb4\
-\x93^\x16)8\x86\xe58\xb0\xa1Q\x12\x10\xe6\xf4\x91\
-\x83updkl`$\xb65+\x01\xa1\xc2R\xd9\
-\xef\xc4\x8c:\xea\xa4\x8c\xda\x1c\xc1Q\xef\xeb\xf5G\x1b\
-\xf9q\xab\xed\xff\xbc1\x13r\xbd\x8c)\xb16\xffq\
-\x1fU~\xcb!\x9d\x99@E\xedK\x84t\xf9\xea!\
-]\xcb\xde!}S\x91\x8f\x03 \x93\x80[Z\x1f\x93\
-\x98\x14\x8a\x0a\xaf\x07@\xa6\x00$\xa5\xe8\xb0\x04.\x99\
-\x1b\x01\x90u\xc5\x7f\x1f\x00\xd9\x15,\x90\xca\x8607\
-H\xd3B\xeb\x0d{@aK\xef\xb0\x02Lh\xb9!\
-,\x0c2\xa5\xf4\x01\x90#\x80<}=\x00\x8e\xe0\xe8\
-2c\xfd.\x00\xb21\x84&\xb7\x00\xd8\x09\xb9\xf7\xd3\
-^\xc6\xe8\xd2#8:\xffX\xbd\x0b\x80\xe3\x80\xba\x98\
-4\x8e}\x8c?#G#6\xd29V\xef\xc8\xef\x02\
-\xe0\xa8\xaaN\xd8\xa8\xd5\xb1\xe1Q\xfb#6\xdaI/\
-\x0b\x1dJ\x1am\x04@F\x83@\xd4.\x00\x8e\xe0\xc8\
-\xd6\xd8@\x17\x00Y\x10\x881[\x00\xec\xc4\x8c:\xea\
-\xa4\x8c\xda\x1c\xc1Q\xefc\xfd.\x00\x8ec\xea\x02\xe0\
-~\xa7B\xf2?\xeeT\xe8\xd3!\xbd\x0fe_\xfa\xea\
-c\xff\x0b\xa3'\xaf_8\xfe\xf1\xae_\x0a\x01R!\
-\xe4C$:\xca]\x13\xdd\xdf\x0f\x81\x99\x87$\xd0\xff\
-\xfe\x09\xecO\xb7\x0eL \x96\x7f<\x0b,zH\x02\
-\xe3\x1f\xd0\x02\xed\x90\x04\xd2\xae\x04~K\x22['\xb8\
-\xcbA\x93\xc8?\xe4%'\x97\xc2\xbb\x92\xf8m*\xf6\
-\x93$\xe6\x97\xb7\xc4\x03\xdeo\x9f+B\x98&\xcb&\
-\x13_KS\xdbI\xd4\xafi\x89*\xa0\xa1\xe1\xdc\x8d\
-\x8f\xd0\x80C\x95\xbb\xf1\xb9C&%Z7\xbc$\x10\
-\xb6\x08\xebF\xe9:\xf1\xb8\xc7\xf4\xed \x84 \x16\xcd\
-v\xe3\x8c\xd8\xd1\x92f\x08$j\xc1Z\x7f\x85\xa3:\
-\xc5,@\xdd\x8c\xd1f\x8c@\xa1$\x87\x08\xabR\xbe\
-\xe9\xe1\xf3\xf40'D0Q\xcdC*B\x88\xf2k\
-\xcf\x06\x12\xe9An\xf0\xf4s\x19\xfd\x98v\x9f\xcb \
-\x07-\x86\x88\xa9\xbf\xad\xfb:^\x00\x8b\x84\xcd\xd8\xc0\
-\xa5\xe0r\xa61\x08XRK\x19\xe0\x82%\xb3\x82\x02\
-\x1c\xe9<c\x07\x0b7\xb1\x8a)D\x12\xd7\xea\x09\x84\
-\x11e\xa8^\x08\x02\xadH\xce\xd4\x00\x855l\x04\xcb\
-,\x18B\xcd\xb1\xac\xd5\xcf\x02\x1e\xc8\xc5[}\x0bH\
-s\x0b\x9e\x11:X\xd1R\x8b\x0a\x02\xa9\xa8\x94\x07\xb0\
-\xf6\x89\x04\x982\xfa\xfa\xb2\x14\xf4 'W\x08?t\
-\xc7\xc7\x9a}\xcf\x9b\x9e\xd4\xc9u\xc6\x0a\x99\x051\xda\
-\xf4\x17\xb89\x935\xb0\x109g\x05\x15\xeek?\xd0\
-\xc69\x90\x996\x13\x02qW\xf3\x01$d%\x9d\xc9\
-\x927\x8c\x87\xdaR \x8a\xf1J\xb6\x02;\x91\xe8\xcc\
-\x10\x14\x0b\xb9\x8d \xcd<*\x88\xe4\xa3\xecH@R\
-\xd4\x8131\xd0\xac\xdbpm\xe9\x1c\x85\xablc0\
-\x22\x92{\x8c*\xe6\x02\xc4\x1b\x95C $3\xca\x83\
-\x94\x18\xc1|\xe8O\xdc\xd7\xee;\xee\x08^\xc8\x0a\xcd\
-$@\xd9\xa3P\x15]\xa08\x15\xe3\x99\x08D)\x82\
-\xad6\x01\xe7P\x1f\x9b\xba\xee\xeb38\xa2\xb5\x98\x13\
-`J\x1c^\xc1fx\x85$\x9a\x09\x9a!\x9bn\xb5\
-\xd6\x9f\x8f\xaa\x15\xab\x0e\xbdb\x07\xb1{Uj\x02\x9b\
-&G\x83[\xafHj\xaf\x08\xcc(\xb2\x82\x05\x92I\
-\xdd\x96`\xebe1\xb26U\xbeB\xdc\xee\x91?-\
-\x11I6\x9ey\x82\x9b\x0bQ\x05\x11\x88B\x93fA\
-@I\xa1Z\xe5\xc6\xca\xa4\x83\xc1\xf4\x9e#5P5\
-\xe4Y\x08\x14\xcc\x94\xac\x18\x83\x99\x96|\x00[m\x5c\
-\x9ae_[\x022\xd9(\x1e\x04q\x05\x1d\xd4\x9b\xf8\
-\xf5.\x89w=\xffq\x85\x88\x8cc\x19*\x86ZJ\
-3\xd3\xc2\xaa\x1c\x83\x08N\x16j\xc4\x94\xb0T\xabX\
-B\xac|d}(\x08dF\x85\x9b\xba\x884Vc\
-\xc1\x92\x88>(\x81\x15\x8d\x1e\xabe\xfbm3\x22\xdf\
-2\xc7\xfa\x0f\x91lK\x80\xbb'\x1d8\xd9\xf2WO\
-\xb6\x98\x07\xba\xf5F\x84\x8fr\x14\xb9\x03\xb3c\xf6I\
-\x8a<A\x02\x93\xd7\x93\x14\x05\x82\x12\x16[\xcfR\x14\
-\x04\x85\xc4r#\xd8gBJ\xb2\x8fij\x1d\x1c\xd3\
-\xd4\xd8@\x9f\xa7\xc8\x14JfYOS\xc4\x01\x1c\x16\
-\xde\xa5)B\x10#\xdc\xa8_\x04\x94]J\x8c\xa9\xea\
-\x1eT\x8c1[\x8d\x0dl\x8e\x80\x04,R\xcbz\xba\
-\x22J\x88L\xd3\xf5tE\xcc\x80\x18\x22}\xc6\x22\xb6\
-F-\xd1\x98\xb1V\xa0ht\x19kl\xa0OZD\
- E\xd4\xc7\xa4\xb5\x0e\x8eIkl\xa0O=$\x0c\
-\xce\xc4\xb4\x9e\xb7H\x0b\xb8\x8a\xfaz\xe2\x22G`{\
-\xdc@ d\x10\x8f\xa9k\xc4\xc6\xcc5V\xdf\x1c\x80\
-\x05$K\xe11y-A,\xca\xb9\x9e\xbc\xc8\x12\x94\
-\xc4\xb2\xcf^d\x05B8c={\x913\xa4\x90\xfa\
-\x98\xbd:;\xee\xd3\x17)-\xbb\xd6g0R\x03\xc9\
- [\xcf`\xa4\x09\x9eJ4f\xb0\x15(,\xd2\xa5\
-\xb1\x11\xees\xd9\x00\x07Z\x978H\x03XK\x9f\xcb\
-Hui\xf7}:\x22e\xb0\x08\xb1\xf5lFR\xc0\
-3\x84\xeeAmX\xac\xcc\xb6\xaf/\xd6\x14\x98\xba\x9e\
-\xceH\xa4\xb9\x0bF\xd7+\x91G#\xf8q\x0d\xee\x13\
-\xdb\x00\xa3\x17ZOl$\x0aJ\xea:f\xb6e\x0f\
-r\xe5O]\xc7\x028\xc3\xba\xd4\xd6\x06\xc6\x82\x22c\
-j\xebT\xd6e\xb7\xdd\xaf\x1e\xf8\x7f\xde\xd5\xc3\x964\
-\xfd\xed\x9ax\xffIs<d\x9a\x9ez5\x8c\x93&\
-\x9b\x99xwl\xfb[_\x9c\xf2\xc4\x9bc\xc2\x19\x9c\
-\x91\xad\xc8\x84\xf4]\xdf\x1d\x1b\xef\x863\x01\x0aq\xff\
-\xb6\x8c\x10H\xb8G\xf6o\xd6 \xb8k\x1aww\xc7\
-\x0bCVPt\xc7g\x87\x81E\x98\xb9\xcc\x08\x0a\x19\
-\x16\xa5\x03<B\xccl\xbb=\xa6\xad\xbe\x8b!L\xb2\
-\xa6\x0e\xcc\x99V:\xda\xacT\xd4\x12\xa3\xa3- K\
-\x08St\xb4\x8dew\xa5\xcdY\xbc\xd2v\x10\xba\xb0\
-|U\xba\x88\x02\x84\xc5s\x07\xba\x18\x81S2h\x82\
-\xae}\x07\xefn\xb1\xd3\xe0\x9dv\x0c\x1a\xfb?\xc8B\
-\xe0FN\x5c\xaaN\x7f\xbbt\xd9\xc1\xe8\x9a\x8e\xb1\xae\
-\x96_$\xc6\xba:>\x11c9U\x952\xf6\x8a\xb1\
-\xdb\xf3\xf7\xf4{XQ\x8c\x90\x96o{\x8c{\xf3\x02\
-\x16\xe2\xe4^\x7f\x0a\x14\x09cy>\x91\xaawU\xf3\
-xR\xc1`V4\xbd?\xa9\xa0\x04R\x93\xec_\xd9\
-b@\xc50\x19O\x1f\xb6\x96=\xed\xca~:o\x17\
-7\xda\xc5\xa6\x8a\xab\xfcb\x17l\xe2\x1f7;\xf5\x96\
-}q\xb5mo\xb1O-\xc5\xb1e\xfd\x98\xefj\x0f\
-\xa7\xa5\xc5\x16i\xf4\xb44\xe7m\xd2\x0e\xb5\x16\x01[\
-\xe4\x14Kl\x89\x9f\xcfR\xc4~,\xb1%\x7f>K\
-\xa7\xf4%YrU\x9f^\xd5Bs\x1f[j\xdb\xe7\
-\xb3\xe4j8\xc1\xd2\x8e\xd2\xf0\x10,\xed\x9e\x1c&i\
-t\xe2}h|\xf9*q\x0f\x1a\x9dt\x82\xc6Ii\
-\x87\xa7Qh\x9a%\x91=\x5c\x12%p/\x96\xc4>\
-\x9f\xa5\x97\xafl\x81_\x90\xa5\xe9\xf0\xee{\x86w7\
-\xb7}X\xd2\xd8\xc7\x96\xce\xaa5})\x96\xd8\x1c{\
-\x96&:c\xe5\xb1\x81LP8\x06n\xe7\x8e\xa5)\
-iy\x86\xb8\x934\xda.M\x0fgK\xa1\xd3\xb6\x14\
-\xfe\xf9\xb6\xf4r\xcfS\x05\xf7\xc8}l\x89\xea\xf6\xa5\
-lIXbzA0)\xfbx\xdc\xe2e\xbe\xfc|\
-\x96\x84\x95~kI\x90K\xc1)\x96j\x99=r\xdc\
-\xe9\xb0\xed\xc8R/m\x9f\x1cG\xed\xcf\x97:\xa1\x12\
-d\x9a\xb4%\xe4=r\x1c\xe2.\x1e\xf7\xd9M \x90\
-\xb9\xb8\xb8~\xf7\x89e\xaf\xb6\x0e\xa0\xec`\xc4(\xb4\
-\xb5Igv\xa5\xca\xd3\x1e\x9d\xf5\x0c\x0e\x0e\xd9\xc5\x89\
-\x90'\x92\xfb\xeeb\xc5\x87?1e'\xdbW;\xdd\
-\xbeN(\x9b\xca\xde\xeb\x8eN\xafg:\xbd.\xea\xf4\
-\xea\xaa;\xae\xd3:q\x0d;\xb5\xdc\xd8\xf4$\xde\x13\
-Q\xc9\x83>o\xf6\xa0\x9f`\xea\x97\x13\x12\x82 \x0e\
-\xf5\xa9\x99\xab\x8a\x96\x04\xf2\xa0\xb0\x9d\xd6\xac!W\xb3\
-\x90\x875k\x8a\x13\xc5L\x1c0\x8bV|N\x90a\
-%\xb0<\xffU\xd9\xcc8\xe4ZX)\xa0\xc4e\x86\
-\xe0\x8ai61\x0d\xd4\xa9\x88\x09\xb0h)\xda\xa9\xa8\
-\xa2\x84T\xd1NCd@YH\xa4\xd3\x10\x07 F\
-\x9an>\xb8yPF\xeb\xf6\xd4\x03\xa8^\xf0\x90\x0b\
-\xd2\xb8y\x88\xd8\xca\x8e\x98\x95\x0b\xc6\x8c$\xc1]\xb2\
-\xcc\xe6\xa2@\x89Q>E\xf5\xf5\xc9Y7\xe1\xa5\x0c\
-\x81(\xc6\xfd\xe4\x18\x0aXb\xf7`\xf4XtD\xbb\
-\xa2[f\xe7\xbe\xac-\xa3>i\xcb\xc5\xf7\x88\x0cR\
- \x94\xca\xe6;\x95$`\x9a\xd2\xdd0\xd9V\xb6\xa2\
-s\x83\xc2b\x9c\xb1\x9b\x93\xa8\x83\xcf\x18\xc8\x16s\xfe\
-\xa2\xce/\x5c\xec)\xc2\x02e\x0f\xc2\xe6\x8eP\xd4\xd8\
-\xa8g\x0c\x0dR\xd0zG\x9d[\x01%c\xb5\x8e2\
-5p!\xc6\xf85c_\xa0\xed8\xfc\xfdoy}\
-\xa0\xc9\xdbl\x13\xa9\xa7\xbb}\xf7\xeb\xd2u\xa8T1\
-}c\xb5\xd1Fd\xf0\xf8\xa6n\x10`j\xc1\x9c\xb8\
-\x01\xdc\xdd+\xfeUY+\x87eM\x89\x0b\x0a\xb7\x14\
-KEL\xf3\x8b\xe5X\x81\xe2\x84\xdaS\x9a\xc0bE\
-\x7f]\xc7%\xfa\xc6\xe9\xc19\x95\xdf\x10\xa7V\x00\xc9\
-\xbd\xc4\xe6\x0a\xc1\x8e\x8aB\x1d\xa7.\xa0\xa6\x86}R\
-&\x02\xb1B\xfa\x84\xf3O\x9d}m.k7\xcf\xec\
-\x16\xb5\xdb\x89\xae\x03\x9c\xd8\xa8\xb0N\xac\x048.\x19\
-8\xb5\xb6`C\x8bB\xa3e\xcf\xfb\xc0[\x17I\xdc\
-\xb6F\xe2\x9c\x0c\xac_#\xf1\x80\xa4\x89jy\xe2<\
-\x5c\x85ib\xd5\xc9~\x89\xca~\x1d\xcb\x89\xb5.O\
-\xbb\xaaO\x98\xd8\x84\xbdO\xfa\x11WB\x0fI\x1a;\
->ii\x94\x13+Nw\xabSO\xac\xdf\xdd\xad\xf5\
-=ep\xd3~X\x12I\xb1\xac\xfc\x10A\x0b\x06\x1d\
-\x8a\x9c\xe9+;\x15\xd2\xce\xa2\x8aA\xa0\x11\xd2\xc6z\
-\xb1\x08\x22\xa4\xfa\x895eO\xb7\xd6<\xedj\xfe\x92\
-\xa7t\x18\x0fl1\xc8O[\x0cn.#\xecAI\
-}\xde\xcb\x84\x10\xd1\xf2hubS\xce\xde`\xd2\xc1\
-4(\x7f\x19\x09\x04\xc9\xee\x143\x01\xd1t\xe1\xfc\x9a\
-^\xb4q\xfa\xee\x04\x94\x8e\xee='\x0clE\xc9\xbe\
-\x16)\xf3\x82P\xac\x98\xcd\xe6\xd1~\x09\x17\xf9\xaa\xac\
-ho)\x01\xa4\x18E:V\x86\xe4\x84a\x1b\xac\x98\
-B\xbaYt\xac\xb0\xe4r@\xbf\x88\x96\x12\x87s\x98\
-\xe9\x8bn\x11\xf6\x89\xa5\xed\xbbe\xf0'\x96\xcc\xef\x96\
-\xd7\xdf+\x0f}\xcd\x91\xf3\xbe\xc9\xa5\xffLB\xff9\
-\x85\xe9\x87\xf4\xd8Y\xf7y\x06\x8f\x9d}\xfa\x0b\x19\x87\
-~\xcey\xfb\xdc\x16m{l\xa7\xbf\xcf\xb2\xf7WV\
-\xfa\x9bl>\x84\x99)a`\xfb\x7fg\xa5\x17\xc78\
--\x8ev\xfc\xca\xca\xb40>\xd8\xedQ'\xdc_%\
-\xfd\xc7\x81\xa6U\xc2{s\xd4\x7f\x1ch'a\xba\xef\
-\xc7h\xd8\xd1\xa7\xbf\x12\xd5\x7fUj\xfa+T\xfdW\
-\xab\xf6sd\xcc\x1d\xbe)\xf5U\x1c\x19\xf7\xb7\x9a\xfe\
-Cd\xd3\x8a\xc4\x9d\xacf\xff\x0f\x91\xf5\xc2\xf4`\x9e\
-eE\x7f!G\xaf\xea\xb6\x13GV|\x0f\x8e\xa6\x85\
-\xe1vay8\x8e2\x7f!G.\x8b\xb2#G\xf8\
-59\xe2\x03r\xc4\xbf4B\x0f\xdb.\x1c\xa5\xee\xc1\
-\xd1\xb40\xdc.\xcc\x0f\xf8X\x82\xe6\xdf\xd1C\x09\x8d\
-\x90\xf6A\xda\xfak\xf5\xbe\xf9\xf8R\xb9\x04h\x12\xdb\
-\x8c\x18\xc8M\xd5\xeb{\xa5\x84\x06\xa4\x89>s\x02\xd2\
-\x88\xd4\xa3?U\xd4\x81\x84\xb4\x81i\x82\xc6\x03\x143\
-g\xc8\x92\xa8~\xbf\xdfJ\xaf~\x1a\x10R1]\xab\
-\xed@\xeaEs]\x8cC\x1a35\xe1\xe2\x10\xc3\xda\
-\x16D\x06\xc9&T[\x13\x067t\xa9`\x82\x9a\xc5\
-\x11;\xc4\xb0\x97\x9c\xca\xf7\xbbR\x1bX\xfe4(\xc3\
-V+/\x01\x06\xc2L\x97#\x11\x08F\xe3Y\x01U\
-\x0f\xb4\xdc\xc2B{\x89\xb5{#\x9f\x87\xe9\xb0\xed\xef\
-\xb4>\xfe\xde\xc8\xe6;\xad\xcb\xdd\x9bw\x17\x8b\x17\x8b\
-\x9f\x16\x97Wgg\xebo\xb9V-}\xd3\xcd/\xd0\
-\x8d\x98p\xaf\x9b\xf1\xa1%6\xf5$\xd7O.\xc6\xcc\
-\xc5\xf3 \xea\x0a\x86F\x0c\xa0\xb3\xd5\x91\x05\x812\x17\
-\xe2\x86\x11G\xe4Q H:\x96\xd2 \x95\xc2\xe9G\
-^Z-\xae\xdc\x04A6j\x0b8[\xddO\x9c\x91\
-\x83z\xc6\xc3^m\xb6\xfd+\x80mH\xb5A\x07t\
-\xc2\xa8\x0d\x22\xa4\xba{\xdf\x8b\x1f\xbb\xdd\x07\xde\xee\xb6\
-=T\x91B\x85\xa2\x0c\xf3\x8a\xe3^Q #,:\
-c\x10\x13J\xe3\xe7\x9b\xecs\xf0n\x9ea*\xf8y\
-T\x7f\xb7<\xa1\x5c66~E\xf5\xf9\xaf\xac\x02P\
-TEn&\x19E$*\xed\x01\x5cH]f\x05\x8a\
-\x17+Y\xf6WEh\xbaR\x0e\xaa\xa8\xbb\x05\x91J\
-\xce\xd2!-B\xb5\x8auH\x14\x8bG\xaa\x90B\x9f\
-r\x84@%*\x19\xdf=\xa1\x1d>\x90v~\xd7\xe7\
-\xa6Zo\xd1\xf2S\x9dA9]n\x9f\x8ax\x06E\
-\x08\xb9\xb4\x18\x91\xc4Tt\x08y\x04\x81\xec\xa5E3\
-,\x5c\xdc\x86 &@li3\x170rb\xaa\xd8\
-*\xdc\xcd\x5c!\x5c9c\xa8\x9d\x18Yk\x1b\xa4\xa9\
-J\xb6H\x16`,\xadM\x22\x84\xe2:hV\x04\x94\
-L9*\xaaCH,G\x5c@0,u\xc0\x04Q\
-m\xc4\xd6j\xff\xb8\x8e\x06\x94BDC\x84\x1bQ\x03\
-\xf2\x12\xc6-d\x9a\x9aZ4_N\x0b\x13\xde>\xf6\
-m\xe8\xe3\xf0\xa7\xa1\xbd\xd6\x9f\x88ur\x90X\xc7\x91\
-`\x86\xaa3&P\xd7\xa1\xaf\xdc8C\xd3\x98\x85@\
-\xa2\x17\x1b\xc6O\x0cA\xc5+j\xa0\xe4\x864`E\
-\xc9f\xe1\x90\x9c$1TF\x14\x5c\xaej\xe2\x84N\
-\xad\xc9h\xd9\xa1\x00F\xba\xb7\xc6\x92*e\x0c\xee\x98\
-u\xc7\x86\x9d4\xca2\xec\x8dE\x7f\x1c\xf6Y\xa0p\
-a\xe1V\xb5\xed#\x84\xa4X+M\x90\x1a$\xcd\xd9\
-\xd9\x94S\xb6\x0ej\x0b8\xfa\xee\xd4\x87\xe6\x102\x95\
-J\xe1\x990\x83\xa2\xb1\xcd\xe6\x02\xe4\x81\x99\xf6\xfc\xb1\
-\x1a\xe9\xb1\x1a\xd7\x17\xa6\xf8\x84\xd7\xfa\xf3\x8de2\x00\
-#\x02K|\xd1\x0f\x06\xf4\x16\xb4\x19\x06\xe8\xd3AZ\
-\x18\xc4\x5c\xa2R\xaf\xcd[\x15\xb3\xb9\x1e\x82%1W\
-\xd4\x00M\x1cc\xddu\x1c\x98\xac\x88m8Y\x14.\
-\x9d\xe7\xa9\x80\x16N\xe5\x0eu\x87b\xac!U\x90\x09\
-\x18\x12F6\x94\x22\xbc\x96\x0d\x03'v\xd1\x16a\xb8\
-8\x16;*\x02\xe2\xca63\x07\x16\xb1\xe0\x95;R\
-\xba\x0e\xbd\x8c\xe2\xcb\x93\x18Qp-h\xdaPt\x1d\
-\xe2\x08\x93\xba\x0eCD\xe3,[\x87\xfd\xe3Vt\xd3\
-\xbf\x99\x85w=\xf5\xb4r\x10\xff\xee\xbb\xe5.\xcb\xf0\
-H\xa0V\xdc\x07\xb4\xa8\x06Y\xd3[\x90\x09S\x1b;\
-gA\x1b0\x0a\xc5\x94\x86%\x17,\xbc\x0c\x84\x85(\
-\xc4\x9a6\xb1`\x9a6\xcc\xc3E\x07L\xc2\x82\xaca\
-&m\xcc\x0d\x0b\x0eOY\xd66N]\xa1\xe2N\xb1\
-,iE\x06\xc8027*[\x89\xb0eeu%\
-\xd2\x86\x16!v\x1e0\xcc\xa1\x93\x01D\x9aKL\x82\
-\xc2K\xc3\x04M4V\x11\xdcK\x90\x0f(\x99k.\
-KJ\xf0\x00q`\xe4Fe\x09\xd5U\xdd\x8aR\xc3\
-\x8c\x0b\x97\xd2[pH\xf8\x06T4\xa8\xe4C\xe6\xd0\
-\xc2\x03j\x89.\x1b%=%t\xca%\xa8\x00\xa2\xb3\
-\xd1\xa6\xa3\xb08!\xafP)\x1e\xd1\xa1\x82\xa0\xce\x85\
-\xac5\x9b\x94\xc9+?MUod\x18hJ\xb27\
-sO\x0b'i\x98%K\xe6\xba\xa78T\xa8X\xeb\
-Ua`%$\x9e\x99Aj\xaaSm3\x12\xc4\xd0\
-\x9cf\x8ePP\x92\xf4\xc8\x11(\xd2\x88[*7J\
-B?Rjn\x8c\xe2\x83\xeb&\xb35\xcbp\x84\xd0\
-b*\xad\xa4\x17\xb7\xe5\xe9X\x11\x1b0\x02\xe1\x94\xb0\
-&[$8K\xf3hcV\xd4e\x8f\xc4\x8b\x8a\x0e\
-\xa8\xa9\x96\xaa\xf2\x86zI\x13i\xa8\xaa\x9a\x97\x16\x10\
-Z\xd78\x1b&\xaa\xc5}\x15$\x04\xa9\x8b\x12\x0d\x15\
-&\xcb\x01E\xd2hcw\x06\x0cE\xb5\x99(P:\
-\xd2\x93\x11%\x85)\xe8\xfe\xa4\x81\xa9\xb1\x1c\x19\x8c1\
-x\x1fFX\xc5\x18R\x22\x9c\xbbX\xdaP\xc7\x94\x18\
-\xd0,\x91<`\x91\x962`\xa1\x1a\x0d*HE\xbd\
-w\xf2e\xba.\xbcD\x991\x1bf\x82\x1a:`F\
-X|k\x88\xf8q\x1b\xba\xe5|\xc5w\x8fg\xfe-\
-\x9e}\x8bg\xdf\xe2\xd9\xb7x\xf6\x1b\x8eg\xc6\xbas\
-<\xdb\xe7\xfa\xeb\xfb\xe3\xdb\x9f^\xff\xf1w\xff\x1f\x87\
-I\xee\xb2\x11\x94\x00\x00\
-\x00\x00\x19\x91\
-\x1f\
-\x8b\x08\x00\x00\x00\x00\x00\x00\x00\xed}mo\x1c9\x92\
-\xe6\xf7\xf9\x15:\xcf\x971\xae*\x14\xef\x11\xf4t\xcf\
-\x027\x83],\xd0\x8b;\xdc\xce\xe0>.d\xa9d\
-kG\x96\x04In\xdb\xf3\xeb\x97\x99%\x89E)[\
-Y\x96J\xee\xde\xeb.\xb7\xdb\x95O2\x18\x8c\x87\xc1\
-\x88L2\x8b\xf9\xdd?}\xfep\xba\xf7\xe3\xea\xf2\xea\
-\xe4\xfc\xec\xfbW\x04\xf8jouvx~tr\xf6\
-\xee\xfbW\x7f\xfb\xeb?/\xf3\xd5\xde\xd5\xf5\xc1\xd9\xd1\
-\xc1\xe9\xf9\xd9\xea\xfbWg\xe7\xaf\xfe\xe9O\xbf\xfb\xee\
-\x7f,\x97{\x7f\xbe\x5c\x1d\x5c\xaf\x8e\xf6>\x9d\x5c\xbf\
-\xdf\xfb\xd7\xb3\xbf_\x1d\x1e\x5c\xac\xf6\xfe\xf0\xfe\xfa\xfa\
-\xe2\xcd\xfe\xfe\xa7O\x9f\xe0\xe4\x06\x84\xf3\xcbw\xfb\xaf\
-\xf7\x96\xcb*y\xf5\xe3\xbb\xdf\xed\xed\xedU\xb5gW\
-o\x8e\x0e\xbf\x7fuS\xfe\xe2\xe3\xe5\xe9X\xee\xe8p\
-\x7fu\xba\xfa\xb0:\xbb\xbe\xda'\xa0\xfdW\xad\xf8a\
-+~8(?\xf9qux\xfe\xe1\xc3\xf9\xd9\xd5(\
-yv\xf5\xfb\x8d\xc2\x97G\xc7\xb5tk\xcc'\x19\x0b\
-Q)e\x1fy\x9fyYK,\xaf\xbe\x9c]\x1f|\
-^\xf6\xa2\xb5\x8dS\xa2\x8c\x88\xfb\xf5\x5c+\xb9]\xa9\
-7\x9fO+\x13?\xd9\x98\xf1\xec\xa6\xf6\xca\xfeE\xfd\
-{'p\x0b\xc0\xd5\xf9\xc7\xcb\xc3\xd5q\x95\x5c\xc1\xd9\
-\xeaz\xff/\x7f\xfd\xcb\xdd\xc9%\xc2\xd1\xf5\xd1F5\
-\xb7\xe4wz\xbb\x1e9;\xf8\xb0\xba\xba88\x5c]\
-\xed\xdf\xe2\x83|\xef\x0e\x15\xa8=|t\xfd\xbe\x1er\
-\x8e\x87\xefW'\xef\xde_\xb7\xe3\x93\xa3\xef_U\x83\
-\xd9\xd1\xc7\xe3\xdb&\xbd\xb9\xab\x08Ax<u\xabg\
-\xf3\x94\xde\x93::?\xac-\xab\xed~w\xbe\xbc\xb8\
-\x5c\xfdxr\xfe\xf1j\xf9\xe3\xc9\xea\x13T%\xff\xe8\
-\xab9\xffx}\xf1\xf1\xfa?V\x9f\xafWg\xeb\xfa\
-\xaaa\xcd\xca\xf5\xe9Q\xee\x0e\xec+X}\xbe8\xbf\
-\xbc^\x1e\x9f\x9c\xae\xd6J\xf7\xdf\x9f\x7fX\xed_\x9c\
-\x9cU+/\xcf\xeb\x97\xc3\xab\xfd\xf3\xcf_\xde\xad\xce\
-\xf6\xab\xc4\xe9\xc1\xdb\xd3\xd5\xfe\xc1\xe1u\xd5u\xb5\xff\
-\xa0\x81\x17g\xef&\xab\xff|tQ\xbb\xd3\x03l\xf2\
-\xf4\x97v\xfaO\xf5\xfcw\x1fV\xd7\x07G\x07\xd7\x07\
-\xf5\xfb\x0d\xbd\xb7\x88\xc6X\xa2\x96\xa9\xce\xfb\xe6\xff\xfe\
-\xe5\x9f\x87\xa3\xf1\xf8\xf0\xf0\xcd\xff;\xbf\xfc\xfbx8\
-~\x86\x02\x07o\xcf?\xd6\x8e\x1aen\xca\x1d\x1d\xbe\
-\xa9\x0e\xf4\xe1\xe0\xfaO'\x1f\x0e\xde\xad\x06O\xfd\x9f\
-\xd5a\xbe\xdbo'\xba\xc2\xd7_.V\x15\xe8\xaa\xbd\
-\x5c\xad=qj\xf0\xd6\xff>\x9c\x0cB\xfb\xff~}\
-rz\xfa\xaf\x83\x92W{\xfbw\xed\xdc\xbfih\x05\
-\xc6\xc3fG=\xb85s<\xbas\x88\xa1c\x8e\x06\
-~+\xba\xc9\xde\xa7\x93\xb3\xa3\xf3O\xcb[w\xf4\xc8\
-W\xd3%n\xfd\x972\xee\x97\xb8\xa8\xcd\xbbz\x7fP\
-K}\xff\x8a\xa7N\x9e\xd7\x01R\xed\x19\x1c\x15o\xce\
-\xbf\xfbxr\xb4\xba>?]]\x1e\x9c\x0d$\xd0\xc6\
-\xa9\xcb\xaaj\xf2\xcc\xf9\xdb\xff\x5c\x1d^O\x9f{{\
-~y\xb4\xba\xbc\xd3D\xf7N\x1c\x9e\x9f\x9e_~\xff\
-\xea\xf7>~nN\x0dm\xbb=q<~^5w\
-y{p\xb5\xba9\xbcz\x7f\xfe\xa9\xb6\xaa\x82\xd7\x97\
-\x1fW\xf7-\xfc\xc7\xf9\xf9\x87Aa\xa2z\x86\xdd?\
-}\xf8\xf9\xfbWKc\x08\xe7\xd0\xf2\xe0lmk\x09\
- \x8e\x92\xf1\x13\xd4\xd7\x0a\xf0'NUi5\x7fP\
-\xe9\xc7\xcb\xcb\x1a\xfb\x97\xa7\x07_V\x97-\xae\xdc\xb8\
-\xcb]\xb1\xc1\xa4[\xb7\xac\xdeV\xf9\xacc\xb4bC\
-uw4\x0c\x00+\xf3\x1d\xf8\xe3\xc9\xd5I\x1d\xc0\x8d\
-\x8b\xf1SG~\x05\x8f\xee\xa1Cd\xacY\xb0\x1a\xc0\
-0\xf0~\xf1\xf9\xfe\xa9/\x13\xa7V\x1f.n\xce\xd6\
-\x93\xd5\xef\xd7~\xfd\xd0\x95G\xfchu|\xd5:m\
-8b\xc7|`\xebE\x8d\x95\x17\xab\xc3!\xdd\xdd\xa8\
-i\xc1rm{_T\x1a\x0b-\xde^\xfc\xc7\xd0\x15\
-{o\xf6\x5c\xeb\xffh\xb2\xc4\x97u\x09B\x1c\xfe\xc1\
-\xc92\xff\x18\xe3\xfeT=}\x13\x96\xe7\x97'\xefN\
-j@\x1e\xcb\xe9\x9a(\x8f^\xa6Z\xbda\x9c\x96\xbb\
-@\xf1]\xcd\x89\xab\x83\xcb\x7f\xb9<8:\xa9\xde\xb0\
-)\xd0\x9f\x11\xadRw\xc1\xe5\xea\xfa\xfcb\xf8\xdeJ\
-\x0f\x88\xa85\x9d\x95\xbb\xeb/\xa7\xab\xf5\x99\xe58\x82\
-\xde\xfc\x1e\xc7\xcf\x1fG\xe8f\x18\xbe\xd9\x149?>\
-\xbeZ\xd5\x10\x83\xb5\x81\xf3\xca\xe4\xeb\x95\xe1\x842j\
-l\xec\xf7F\x7f-G\x5cl\x96#.\xf1x\xb3]\
-V\xe5\xf8x7\x1cq)\x8f+[G\xb4o\xca\x91\
-\xe3<G\xce\xf3\xcd\xde\x19G\xae\xbf8\x8e\x90\xe79\
-\xc2\x99f\x1f\x1e\xadV;\xe3\x08\xfdqeG\xbc\xca\
-\xd5\xd1\x0e8\xaa\x17\x88\xd7\xab\xcb[\xc1!-\x22\x10\
-2\x06\xd2m\xd6l\xd7\xc9\xc0\xa8\xa4\xd8\xe2\xc0\x97u\
-y\xd6b\x8ew\xc6\xb6\x0bi`-\x85\xb8\x0b\x8ck\
-\x8d\x22\x19\x8d\xf3\xe3\xd5\xbf\x1c|\xbc\xba:98\xfb\
-_\xa7\x1f\xd7\xad\xe9\x83o5\xfbtuX\xab<8\
-\xfdt\xf0\xe5\xaa#\xe7\xe8/\xf5R\xf5\xe0\xfa\xf6\xaa\
-[\xdd\xcc\x0b\xbd\xeai\xed5\x88d\xd9`f\xdd\xa4\
-\xaf\xf4\x1aW\xd49\xafq\x9d\xebHLZ\xc9j'\
-^\xe3\x8a\xf9\xb82\xc98Z\xe9\x8c\xb2]\x8e,\xaf\
-\x9fY\x8e\xdcg\x9a]\x8e\x0f\xe9\x98w3\xb2(g\
-\xb2\x98e\xac\xde\xda\x16\xca\xc0\xe6\xd5\xb9\x07\xce\xf4\xff\
-!\x1fXN\xa8{\xc6@\xee\x07\x1a\xbd\xd0@37\
-\xb6\xe4||\xa0\xd1\xd7\x0e\xb4\xcf\xf4\xfd\xab\x10\x08e\
-\x97l\x81\xa6\xa2d \xe2\xa6-\x0e\x7f\xe6Z6\xa1\
-H\x11)\xadlE\xc9!\x0c\xc3\x1a\xfa\xd09M\xa5\
-u\xce8Y\xf1\xe6\xfd\xe5\xea\xb8\xdew<H\xa1\xad\
-\xdc\xbb\x1b\xf0og'\xd7u\x9a\xe4\xe3\xd5\xea\xf2\xdf\
-\x87\xa9\x86\xff}\xf6\xb7\xab\xcdk\xec:\x8ds\xf4o\
-\xab\xeb\xf7\xe7Um\xadt\xe0\xb4\xf20o\xb9BX\
-o\xb5\x02\x87e\xee\xd0j\xde\xce\xeab\xdf\xcaj\x8a\
-\x02\x14\xcc\xda[\xce\x02a\xe9\xbd\xe5\x14\x02%1\xa8\
-\xb3\xdc\x0dT\x93\x88\x1f\xb7\xdcwny_\xea\xaf\xf5\
-\x1e\xf8j\x98m\xa8\xf7\x5c\xc3\xd7\xd3:\x91\xf8\x87\x12\
-\x0b|\xbd\x15\x0d\xc5A=\xd5;\x1a\x5c\x80$\x8d\xac\
-\xa3\xc1\x18\x98R={\x1a\x1c\x98U-\x1e\xa1\x81\x8b\
-\xc9\xae\xdc~\x9e\x86:\xefry\xf2\xf9\x0f\x04\xc8^\
-\x1c}\x81\xf5O;Z\x8a\x03\xa3F\x94\xc5\x92 \xc5\
-<\xcc\xb7\x22+c\xcdU\x1f#\x96\x08\xe2\xa4\xc9\xe6\
-\xbd\xd7\x90CPj\xf4\xe3\x05\x1d\x94\xc3\xfcq\xba\xfc\
-\xff\x03\xba4\xa0xjp?\xc4|\xed[\xd1\x91\xa5\
-\x05\x98\xfa\xb2\x15M\x1b\xb9\xb2\xf28Ye;\xb2\x90\
-\xbf\x09Y\x86\xf5#\x03Y^DYh\x83\xac\xa1\xc2\
-\x83\xd3\xfbd\x0dSC\xb4a\xfa0\x19\x14\xcd\x03\xea\
-\xd4\x8d\xb5\xa3\xe3\xa1p&dRHC\xab\x081\x81\
-*\x93tl\xf5*\xb9\xf8V#\xd1\xebggl-\
-\x1f\xd0\x85n\xee\x0bF\x07\x09\xa7\xa8d1\xa8{\xf1\
-\xdc\x8a\xac%9\xb8\xa7fv\x9c\xa5C\x0ezl\x93\
-:\xc7F\xd2\xb4\xe8q'\xfa(w\xb9\x95\xa7\xa9\x96\
-\xddq\x87\xc0\x96\xeeXIB0&\xd6\xd0\xe1kr\
-\xa8\xa8.\x10\x94\xd0\x91hA\x82\xc0R\xd4\x16\x86\x80\
-fVl\xcb\x1c\xa0`\x0f\xd2`AO\xe9\x03\x1a*\
-\xb0u\x03\xd4\x80\x065\xc9\x8f\x0e\xd0\xa0\xad\x5cNQ\
-w\xe8r\xa3\xab!\xa4\x13\xaa\xcb\x82\x91!\x86A\xa9\
-\xe2\x94\x1e\xb3\xcc\x8c\xc1:\xfa\x90\xbed\xf1\x9e(\xd4\
-V\x80Z\x81\xf9\x0c\x8d\x8be:\xb0p\x8a\xbe\xde\xce\
-\xecG8\x16\xdc\xd25\x91k_\xcc\x5c\x80\xcf\x8d\xc0\
-:\xa8\xc4@\x89\xb3\x94n\x10\x85CA\xf3\x22\xfd\x88\
-+@2f\x90n\xb0\xba\xbe\xea\x864\xe3v\xc4-\
-\x89\x0b\x0cZ\xea(`\xb04.\xa2\xab\xa5?\x85\xc4\
-\xde>\xc1\xed2\x09\x97\x82_A\xe2\xe1\xe9\xc9\xc5\xff\
-9\xb8~\xbf\xa9\xf8\x16\x13\xb46\xa0n\xc1\xc9v\xb7\
-\xfb\xa9\x8bVW\xbb\xbf\xbb\xbb\x8f\xfb\xe3q])z\
-S\xd7\x90\xfe\xf0\xfb\x87\xd6\xbd\x1e\xcf.[\xe1\xab\xeb\
-\xcb\xf3\xbf\xaf\xde\x9c\xd5\xf5\xe8\x9b\xef\xeb\x05\x9e7\x08\
-$\xa1\xa5~n\xf1\x81\x83j\xe8\x9b\xcb\xf3\x8fgG\
-\x9b\xe0\x7f\x9e\x9f\x9c\xf5h]\xb4Z]\x9e\x9e\xd4\x7f\
-\xde\xe8-vtP\x97\x86./\x0f\xbet\xca\x06t\
-\xbc\xdd\xac%\x81\xec\x06\x9e\xbc+\xad\xac\xfd\xdb\xde\x92\
-\x98\x80\x13\xcb\x82\x0b\x98\xa1+\xef\xfdy@\x19\xcc\xcd\
-y\xc1\x09\xecVx\xc0\x14\x5c\xdc\xa2\xc3\x0c\x0a\x13o\
-\x8a\xffPa\xbd\xf5(c\xf0t.k8\x18\xa8\xb0\
-\xddW\x16\x02\x1a!\xb4Yq\x18\x98&{\x879\xa4\
-p\xc9{\xcaR\xc0\xd0e!\x0e\xccR|]i*\
-D!\xe4\x85\x04\x18\xa6\xa7t`\x01\x8b\xe2\x5c6\xe4\
-\x15!]\xc5\xc6J\x1dA\x02\xdd\x16\xae\x9d\x92\x0c \
-1\xb7^G&(\xa3\x04m\x82\x05A\x0b\x09\xc5\x86\
-x!\x88p\x94\x1c*m\x06\x95*\xafE,\x87j\
-\x9b\xed\xa5@\x88bnp\xd4\xb0Fg\x13\xef\xc9\x0f\
-\x03!\x09\x1c\xe0\xd6U\x9d\xb2\xd6\xab\xad\xe2\xd6\xfb\x0d\
-k~\xd2\xeb\xa2*\x8da\xd9\x0c\x1b+%\x01aN\
-o\x1cl\x82\x8d\xadVA#v\xa8V\x02B\x85\xa5\
-\xb2\xdf\xa9i}\xd4ii\xbd\xd9\xc0\xd6\xef\x9b\xf2\xcd\
-G~\x98\xf4\xfd\x7f\xdc\x9b\x09\xb9X\xc7\x94\xd8\x98\xff\
-\xb8\x8d*\xbf\xe4\x90\xceL\xa0\xa2\xf6\x12!]\xbey\
-H\xd7\xf2\x8c\x90\xdew\xe4\xc3\x00\xc8$\xe0\x96\xd6\xc7\
-$&\x85\xa2\xc2\x9b\x01\x90)\x00I):,\x81K\
-\xe6\xbd\x00\xc8z\xc3\x7f\x1f\x00\xd9\x15,\x90\xca=e\
-n\x90\xa6\x856+\xf6\x80\xc2\x96\xdea\x05\x98\xd0\xf2\
-\x9e\xb2\xa8\xe2)\xa5\x0f\x80\x1c\x01\xe4\xe9\x9b\x01\xb0\x81\
-m\xc84\xf9.\x00\xb21\x84&\xaf\x03`+\xd4\xc6\
-i\xa7\xa3\x0d\xe9\x06\xb6\xc1\xdf\xc4\xbb\x00\xd8\x0c\xeab\
-R\xb3\xbd\xc5\x9f\xc6Q\xc3\x1a\x9dM\xbc#\xbf\x0b\x80\
-\xad\xab:e\xadW[\xc5\xad\xf7\x1b\xd6\xfc\xa4\xd7\x85\
-\x0e%\x8d\xfa\x00Xa\x83@\xd4.\x006\xb0\xb1\xd5\
-*\xe8\x02 \x0b\x021\xe6\x10\x00;5\xad\x8f:-\
-\xad7\x1b\xd8\xfa\xbd\xc9w\x01\xb0\xd9\xd4\x05\xc0\xa7]\
-\x0a\xc9\x7f\xbbK\xa1\x9f\x0e\xe9}({\xb1\xbb\x8f]\
-\xdd\x18M\xdf\xbfp\xfc\xfa\xee_\x0a\x01R!\xe4]\
-$:\xca]'\xba_>\x81\x99\xbb$\xd0\x7f\x1d\x04\
-\xb6\xcb\xad\x1d\x13\x88\xe5\xd7\xe7\x81EwI`\xfc\x0a\
-=\xd0vI mK\xe0oIdr\x82\xbb\xec4\
-\x89\xfc*o9\xb9\x14\xde\x96\xc4\xdf\xa6b\x7f\x92\xc4\
-|yO\xdc\xe1z\xfbR\x11\xc24Y\xa6\x99x\xf9\
-\x9e\x9a&Q\xbf\xa5'\xaa\x80\x86\x86sg\x1f\xa1\x01\
-\x87*w\xf6\xb9C&%Zg^\x12\x08[\x84u\
-V\xba\xce<\xee1\xbf\x1c\x84\x10\xc4\xa29,\x9c\x11\
-;Z\xd2\x02\x81D-X\xeb\xb7pT\xa7X\x04\xa8\
-\x9b1\xda\x82\x11(\x94d\x17aU\xcao\xfd\xf0u\
-\xfd\xb0$D0Q\xcd]v\x84\x10\xe5\xb7\x9e\x0d$\
-\xd2\x9d,\xf0\xf4s\x19\xbdM\xdb\xcfe\x90\x83\x16C\
-\xc4\xd4_\xd6\xba\x8e\x17\xc0\x22a\x0b6p)\xb8\x9e\
-i\x0c\x02\x96\xd4RF\xb8`\xc9\xac\xa0\x00G:/\
-\xd8\xc1\xc2M\xacb\x0a\x91\xc4U<\x810\xa2\x8c\xe2\
-\x85 \xd0\x8a\xe4B\x0dPX\xc3\x1aX\x16\xc1\x10j\
-\x8eeC>\x0bx \x17\x1f\xe4- \xcd-xA\
-\xe8`EK-*\x08\xa4\xa2R\xee\xc0\xda&\x12`\
-\xca\xe8\xe5e\xad\xe8NO\xde |\xd7\x1co\x92}\
-\xcb\xc9A\xd5\xc9u\xc1\x0a\x99\x051\x86\xe9/ps\
-&\x1b\xc0B\xe4\x9c\x15T\xb8\x95\xbe\xa3\x8ds$3\
-m!\x04\xe2\xae\xe6#H\xc8J\xba\x905o\x18w\
-\xd2R \x8a\xf1\x8dn\x05v\x22\xd1\x85!(\x16r\
-k -<*\x88\xe4Mw$ )\xea\xc8\x99\x18\
-h\xd6\xcfxo\xe9\x1c\x85\xabnc0\x22\x92[\x8c\
-*\xe6\x02\xc4\xf7\x84C $3\xca\x9d\x96h`\xde\
-\xb5'n\xa5\xfb\x86;\x82\x17\xb2B\x0b\xa9\xd2\xecQ\
-\xa8\xaa.P\x9c\x8a\xf1B\x04\xa2\x14\xc1A\x9a\x80s\
-\x94\xaf\x22\x81\xb7\xf2\x0c\x8ehC\xcc\x090%\x0e\xaf\
-\xa0\x81\x97B\x12\x83\x0b\x9a!\x9bNz\xeb?\xf6\xaa\
-\x17\xab\x8e\xadb\x07\xb1\xdb\xae\xd4\x046M\x8e\x0a\x8f\
-\xad\x22\xa9\xad\x220\xa3\xc8\x0a\x16H&u[\x83C\
-+\x8b\x91\x8dS\xe5k\xc4\xed\x16\xf9\xf3\x1a\x91\xe4j\
-\x8e'\xb8\xb9\x10U\x10\x81(4i\x11\x04\x94\x14\xaa\
-Uo\xdc\xb8t0\x98\xder\xa4\x06\xaa\x86\xbc\x08\x81\
-\x82\x99\x92\x15c0\xd3\x92w\xe0 \x8dk\xb7\xec\xa5\
-% \x93\x8d\xe2N\x11W\xd0A}P\xbf\xd9$\xf1\
-\xae\xe5?\xdc \x22\xcd\x96Q0\xd4R\x067-\xac\
-\xca1\xaa\xe0d\xa1\x81\x98\x12\x96j\x15K\x88\x9b1\
-\xb2i\x0a\x02\x99Q\xe1\xa1\xbb\x884nl\xc1\x92\x88\
->v\x02+\x1aMt\xcb\xe4\xb2\x19\x91O\xcc\xb1\xfe\
-*\x92m\x09p\xf7\xa4\x1d'[\xfe\xe6\xc9\x16sG\
-KoD\xf8 G\x91;0;f\x9f\xa4\xc8\x13$\
-0y3IQ (a\xb1\xcd,EAPH,\
-\xef\x05\xfbLHI\xf6\x96\xa66\xc1\x96\xa6Z\x05}\
-\x9e\x22S(\x99e3M\x11\x07pXx\x97\xa6\x08\
-A\x8c\xf0\x9e|\x11Pv)\xd1R\xd5-\xa8\x18-\
-[\xb5\x0a\xee[@\x02\x16\xa9e3]\x11%D\xa6\
-\xe9f\xba\x22f@\x0c\x91>c\x11\xdb@-Q\xcb\
-X7\xa0ht\x19\xabU\xd0'-\xaarE\xd4[\
-\xd2\xda\x04[\xd2j\x15\xf4\xa9\x87\x84\xc1\x99\x986\xf3\
-\x16i\x01WQ\xdfL\x5c\xe4\x08l\x0f+\x08\x84\x0c\
-\xe2\x96\xba\x1a\xd62W\x13\xbfo\x80\x05$K\xe1\x96\
-\xbc\xd6 \x16\xe5\xdcL^d\x09Jb\xd9g/\xb2\
-\x02!\x9c\xb1\x99\xbd\xc8\x19RH\xbde\xaf\xce\x8f\xfb\
-\xf4EJ\xeb\xa6\xf5\x19\x8c\xd4@2\xc863\x18i\
-\x82\xa7\x12\xb5\x0cv\x03\x0a\x8bti\xac\xc1}.\x1b\
-\xe1@\xeb\x12\x07i\x00k\xe9s\x19\xa9\xae\xfd\xbeO\
-G\xa4\xf5(Bl3\x9b\x91\x14\xf0\x0c\xa1\x96\xcd*\
-\x167n\xdb\xcb\x8b\x01\x92\xa6n\xa63\x12\x01+\x8a\
-\xd1\xb5J\xa4Y\xd0\x0ckp\x97\xd8F\x18\xbd\xd0f\
-b#QPR\xd7\x96\xd9\xd6-\xc8\x9b\xf1\xd45,\
-\x803\xacKm\x83a,(\xd2R[\xd7e]v\
-\xdb\xfe\xee\x81\xff\xfb\xdd=L\xa4\xe9\xdf\xee\x89\x9f>\
-i\x8e\xbbL\xd3s?\x0d\xe3\xa4\xd9jf~;6\
-\xfd\xab/Ny\xe4\x97c\xc2\x19\x9c\x91\xad\xc8\x8c\x11\
-\xf3\xbf\x1dk\xab\xe1L\x80B\xdc\xffZF\x08$\xdc\
-#\xfb_\xd6 \xb8k\x1aw\xab\xe3\x85!+(\xba\
-\xe5\xb3\xc3\xc0\x22\xcc\x5c\x16\x04\x85\x0c\x8b\xd2\x0e\x1e!\
-f\xb6\xed\x1e\xd3V\xdf\xc6\x11fYS\x07\xe6L+\
-\x1dmV*j\x89\xd1\xd1\x16\x90%\x84):\xdaZ\
-\xd9mis\x16\xaf\xb4\xed\x84.,\xdf\x94.\xa2\x00\
-a\xf1\xdc\x82.F\xe0\x94\x0c\x9a\xa6\xeb\xd9\xc6\xbb[\
-le\xbc\x13>\xcf\xf8\xf9\x07Y\x08\xdc\xc8\x89K\xed\
-\xd3_.]\xb63\xba\xe6c\xac\xab\xe5\x8b\xc4XW\
-\xc7Gb,\xa7\xaaR\xc63bl\x9f\x9b\xb6\xf9\x1d\
-V\x14#\xa4\xf5\xaf=\xda\xd1\xb2\x80\x858\xb9\xd7\xaf\
-\x02E\xc2X^\xcf\xa4\xeam\xbb\xb9]T0\x98\x15\
-M\xef/*(\x81\xd4$\xfb\x9fl1\xa0b\x98\xb4\
-\xcb\x87\xc9\xb2\x87\x13e\xa7\xf2vq\xdbj\xb1\xbb\xb8\
-\xca3\x86`S?Q\xed\xcc/\xd1\x8b\xabM\xfd\x8a\
-}n+\x8e\x89\xfdc\xfeX[8\xaf-&\xb4\xd1\
-\xe3\xda\x9c\xa7\xb4\xedj/\x02\xb6\xc89\x96\xd8\x12\xbf\
-\x9e\xa5\x88\xa7\xb1\xc4\x96\xfc\xf5,\x1d\xd2K\xb2\xe4\xaa\
->\xbf\xab\x85\xe6\x13|i\xfc|=K\xae\x863,\
-m\xa9\x0dw\xc1\xd2\xf6\xc9a\x96F'~\x0a\x8do\
-\x8f\x13\x9f@\xa3\x93\xce\xd08\xabm\xf74\x0a\xcd\xb3\
-$\xf2\x84!\x89\x12\xf8$\x96\xc4\xbe\x9e\xa5\xb7\xc7\xb6\
-\xc2\x17di>\xbc\xfb\x13\xc3\xbb\x9b\xdbSX\xd2x\
-\x8a/\x1dUoz)\x96\xd8\x1c{\x96f\x1ac\xe5\
-\xa1\x83\xccP\xd8\x02\xb7s\xc7\xd2\x9c\xb6<B\xdcJ\
-\x1bMk\xd3\xdd\xf9R\xe8\xbc/\x85\x7f\xbd/\xbd}\
-\xe2\xa5\x82{\xe4S|\x89\xea\xe7\xa5|IXb~\
-C0)O\x19q\xab\xb7\xf9\xf6\xebY\x12V\xfa\xa5\
-%A.\x05\xe7X\xe2R\x9e\x90\xe3\x0e\xc7\xcf\xd7\xb3\
-\xc4\xa5<%\xc7\xd1\xf0\xe7\xa5.\xa8\x04\x99f}\x09\
-\xf9\x099\x0eq\x9b\x11\xf7\xd5U \x90\xb9\xb8\xb8N\
-\x86C\xb0W\x93\x06\x94-\x9c\x18\x85&\xabtfW\
-\xaa<=\xa1\xb1\x9e\xc1\xc1!\xdb\x0c\x22\xe4\x99\xe4\xbe\
-\xbdZ\xf1\xf1O\xcc\xf9\xc9\xf4n\xa7\xd3\xfb\x84\xb2\xa9\
-<o\xdf\xd1\xf9\xfdL\xe7\xf7E\x9d\xdf]u~\x9f\
-\xd6\x99{\xd8\xb9\xed\xc6\xe6'\xf1\x1e\x89J\x1e\xb4\xfd\
-\xec\xc1\xf4\x04S\xdbNH\x08\x828\xd4\xe7f\xae*\
-Z\x12\xc8\x83\xc2\xb6\xda\xb3\x86\x5c\xcdB\xee\xf6\xac)\
-N\xc3\xba\x829\x14B\xe3\xfa\x95 \xc3J`y\xfd\
-\xb3\xd2\x99\xb1\xcb\xcd\xb0R@\x89\xcb\x02\xc1\x15\xd3l\
-~\x1e\xa8\xf5\x11\x13`\xd1R\xb4\xd1\xbeF\x09\xa9\xa2\
-]\x17\x91\x01e!\x91\xae\x8b8\x001\xd2t\xe2\xc9\
-\xcd\xdd1Z?\x8f=\x81\xea\x05w\xb9#\x8d\x9b\x87\
-\x88\xdd8\x12\xb3rY?'\xe0.Y\x16KQ\xa0\
-\xc4(?\xc5\xf4\xc5\xc1Q7\xe1\xa5\x0c\x81(\xc6\xfd\
-\xe4\x18\x0aXb\xa3\xf3x\xb2\xe8qWtbv\xee\
-e]\x19\xf5QW.\xfe\x84\xc8 \x05B\xa9\xdc\xff\
-M%\x09\x98\xa6\x94V\x96'\xcbVtiPX\x8c\
-3\xb6}\x80\xddA\x17\x0cd\xab%\xbf\xe8\xe0\x17.\
-\xf6\x18c\x81\xf2\x04\xc6\x96\x8eP\xd4\xd8\xa8\xa7\x0c\x0d\
-R\xd0\xfa\x81\xba\xb4\x02J\xc6j\x1dgj\xe0B\x8c\
-\xf1s\xc6\xbe@\xdb\xce\xfcg\xacy}\xa6\xd9u\xb6\
-\xf9\xdc\xd3\xd6\xef~^\xba\x9e\x9f*\xe6WV\x1bm\
-D\x06\x0fWu\x83\x00S\x0b\xe6\xcc\x0ap\xb7X\xfc\
-\xb3\xb2Vv\xcb\x9a\x12\x17\x14\x1eR,\x151\xcd\x17\
-\xcb\xb1\x02\xc5\x09\xb5\xa74\x81\xc5\x8a\xfe\xbc\x03\x97\xe8\
-7Nw\xce\xa9\xfc\x828\xb5\x02H\xee%\xeeo\x11\
-\xec\xa8(\xd4q\xea\x02jj\xd8ge\x22\x10+\xa4\
-\x13\x83\x7f\xdb\x15\xcf\xfb\xfb\xda-3\xbb]\xed\xb6\xa2\
-k\x07W6*\xac3[\x01\xb6=\x03g6\x17l\
-\x9b\x10VZ\x9e\xb6\x10<\xb9K\xe2\xd4&\x89K2\
-\xb0~\x93\xc4\x1d\x92&\xaa\xe5\x91\xebp\x15\xa6\x99m\
-'\xfb=*\xfb\x8d,g6\xbb<\xecDg\x5cl\
-\xda\xdf\xb7\x1bG\x5c\x09\xdd%i\xec\xf8\xa8\xa7Q\xce\
-l9\xddmO=\xb3\x81w\xb7\xd9\xf7\x9c\xc3\xcd\x8f\
-\xc3\x92H\x8a\xe5f\x1c\x22h\xc1\xa0]\x913\x7fg\
-\xa7B\xdayT1\x084B\xea<\x8a\x12A\x84T\
-\x7fbS\xd9\xc3I\xc9\xc3N\xf29\x8f\xe90\xee\xd8\
-c\x90\x1f\xf7\x18\xbc\xbf\x8f\xb0\x07%\xf5y/\x13B\
-D\xcb\x83\xed\x89M9{\x87I\x07\xd3\xa0|\x1e\x09\
-\x04\xc9\xee\x14\x0b\x01\xd1t\xe1\xfc\x96\xa3\x08\xa3\xe3\xc4\
-\x09(\x1d\xdd{N\x18\xd8\x8a\x92}+R\x96\x05\xa1\
-X1[,c\xf8&\x5c\xe4\x9b\xb2\xa2\xbd\xa7\x04\x90\
-b\x14\xe9X\x19\x93\x13\x86\xddc\xc5\x14\xd2\xcd\xa2c\
-\x85%\xd7\x06=\x8b\x96\x12\xbb\x1b0\xf37\xdd\x22\xec\
-3{\xdbw\xfb\xe0\xcf\xec\x99\xdf\xed\xaf\xff\xcc<\xf4\
-\xf2\x96\xf3S\x93K\xff\x9e\x84\xfe}\x0a\xf3O\xe9\xb1\
-\xb3>\xe5!<v\xf6\xf9Wd\xec\xfaA\xe7\xe9\xc9\
--\x9azn\xa7_hy\xd6kV\xda*\x9b\x8fa\
-fN\x19\xd8\xf3^\xb4\xd2\xd41\xce\xab\xa3g\xbcf\
-\xa5W\xc6;[\x1fu\xc2\xe7uI{;\xd0|\x97\
-\xf0s9jo\x07\x9aW\xa6O}\x1b\x0d;\xfa\xfc\
-k\xa2\xfa\xd7J\xcd\xbf\x86\xaa\x7fm\xd5\xd3\x062\xe6\
-v/\x95z\xf9\x81\x8c\xcf\xf3\x9a\xf6&\xb2\xf9\x8e\xc4\
-gy\xcd\xfc\x9b\xc8ze\xba\xb3\x91eE\x9f\xc9\xd1\
-q\xfdl\xc5\x91\x15\x7f\x02G\xf3\xcapZY\xee\x8e\
-\xa3\xccgr\xe4\xb2*[r\x84\xdf\x92#\xde!G\
-\xfc\xdc\x08=~\xb6\xe1(\xf5\x99\x1c5e\xf3\x1c\xa5\
-\xef\xf0\xb9\x04\xcd\xdf\x9eJx\xe6\xbd\x0d{\xd8N_\
-x#\xc2\xac\xe3\xb4G;Z\x122\xa0E\xf1\xc52\
-\x14L\xc2\xc5^O\xdd\x10\xcd\xdd<\xf57Z\xd37\
-e?/\x9b\xf9\xe2l\x92\x80X\xbaK\xc5\xb4\x04\x15\
-\xa3\xd7\x93\xb7\xdc3w\xe7\xdd\x9d\xfc\xf4]\xff\xcbR\
-\x89\xfc(\x95I/O%\x81fQ\x1d\x5c\x14\x0a\x17\
-\x13\x7f\x06\x97\xfdLQ?\xa9\xf4\xf3>\xd8\x91/8\
-\xc6Q\x10\x91\x17\xcbR\x00\xb9Hz\xe5\x92\xc1]\x0b\
-\xf9\xeb\xe9Y\xc2\x86\xce\xcfEN\xcf[\xf63\x94?\
-\xeb\x88\xcf\x9d\xae\x92\x92A)\xe18\xbe\xbdI\xa5\xa0\
-\x95\xb9\xd5\xa7\x17zA\xe5\x8b9\xec\xfc\x0a\x08{\xe1\
--I\xdd\xe5\xa2\x0f\x0b\x98R\xaa/\xb4\x80\x93\x9b4\
-\x07\xde\xf6\x8db\xdb\xbfC\xac_\xb5\xf9y\x1f?z\
-rx\x98_y,\x0e\x88,\xc5o\x9e\x1bd\xb5\xa8\
-\xac\xee\xf8\xcdb?\xe7\xb3H^b\xf7o\xe5|\xa9\
-\xb7\x90\xaey\xfan\xffhu|5~\x1b\xb76j\
-\xfb\x17\xa5\x01\x93\x8b\x8d\xb91\xd8%\xa3\xeeaB\x01\
-\xa6\xc5T\x17\x8e\x10\x98\xc1\xc3\xbb\x1a\xc8\xc1\xdc\x19+\
-J\xa0$\xa5\xc8^\x8b_\xce\xa0\x96%c\x13\x93*\
-\x81Ei\x90n\xa8\x82;g\xb1\xcd\x1a\x0d\x02#\xb3\
-l\xea\xf6A\x0b\xd9\xd0\xa2\xac\xd2(9\xb6\xd3@\xd2\
-\xc7\x16\x15\x04T/1\xa0\x09H\xee\xbaWn\xf5\x8c\
-\x98d\x11\xef0^7\xa9\xd6\xb9\x81\xda\x0d\xf8\xe7\xae\
-(\xb8\x97D\xdd\xcb\x84\x12\x81\xa9\x8b\x02\x88\xe1\xea9\
-I\x5c\xddc\xa5\xdb0\xaa\xa2\xa5B\x93[\xae<\x1c\
-\x93\x0f\xb6\x5c\x19\x0f/?\x9e\xae\xde\xac~\x5c\x9d\x9d\
-\x1f\x1dmn\xc2R{\xf6\xb7\xfe\xfc\xe6\xfd\xa9\xd4\xf7\
-g{\x0e\x9fM=\xc9u\xe2\xfd\x22-\xc9\xed\xa6\x8b\
-\x13\xf4\xc6\x22\x04g\xd1\xb1;2@\x19\x8d\xd6hf\
-0\xed\xa5\x83\xa4\xc4\xdavDd\x8ej\xe7\x9d4\xdd\
-v\xe6\x0f\x83t\x06\x8fh\x01J\xabhO\xa8\x03r\
-\xf1B\x934S\xcf3\x83+[\x96\xbd\xc2\x80\x96\x9e\
-c\x9b\x14\xc3\x91&[\xff\xc3\x14:\xc5\xbel;\x9a\
-\xb2|\x1d\xd5\x7f\x5cO\x91\xae+[\x7f\x17\xc9x\xfd\
-\x13]\x10\x0c6r\xbav\xdb P\xe6B<`\xc4\
-\x11\xb9\x17\x08\x92\x8e\xa5\x0c\x90J\xe1\xf4=/\x83\x14\
-WscL\x8d\x03\xd7\xce6\x98\x8f\x03\xc3\xea\x19w\
-G\xb5\xda\xe1_\x01Lr\xad\x15:\xa0\x13F\x19\x18\
-Ju\xf7\xbe\x15?t\x87w\xe4]O\x5c\x1f\xa5\x90\
-+\xf1\x98\xb9\x97\xe3V]\xea.c\xcf%\x88\x1a\x96\
-\xd7\x0f\xa9\xb7\x9fr\xfc@%*\x19\x1b\x8e\xffp5\
-\xedE{#\x1d\xc2\x8c}\xed\x90\xa4\xe8^\xe9`\x02\
-&w\xb4!\xba\xb9`\xa6\x0cQ\xab@\x94\xd4\xac\xa8\
-\x00j\x96\x12{\x8c\x80l\x9a9D2.\x1c\xca\x83\
-4\x11E\xf8\x10\xc9$\x0a\x0a\xad\xa3\x16y\xfa\xa0\x89\
-\x10\x84s\x0c1Y\x80\x9d\x8a\x0d\xa8\x80\xa6\xa1\xed\x15\
-\x011c]cn\xa2\xd4\xb0\x0d\xe9\x1f6\xd1\x00O\
-\xe3\x18\x87SC\x152\xa4\x0c\xb1\x918s@\x04\xcc\
-]\xb8LY>5lbb\xd8LF\xa8\xdcM\x12\
-\xe2H0C\xd5\x05\x13\xa8\xebh'\x93\x02\xa1i,\
-B \xd1\x8b\x95j'\x13CP\xf1\x8a\x1a(\xb9!\
-\x8dXQ\xb2E8$'I\x8c\xc2\x88\x82\xeb\xed\xf5\
-\x9c\xd0i\xa82bA\x5c\x00#\xdd\x87\xca\x92*9\
-\x0c\xee\x98\xf5\xc0\xc6\x834\xca2\x1e\xb5\xa2?\x8c\xc7\
-,P\xb8\xb0p\x15\x1d\x8f\x11BRl(M\x90\x1a\
-$\xc3\x10eSN\x994j\x02l#o\xe6V\x1a\
-!\x93\xd0=\xeb\x18,\x09Z\xca0-!\xe0\xc8\xae\
-\xa6\x13Cp\xe2Zbs\x87\xb4\xe9HX\xe2\xf5\xbd\
-\xfd\xda\x00#\x02K\xbc\xe8\x9b\xab\x9a\x07M\x0dfz\
-d0\x17\x88\x10\xa7\xd1\xf1\x1d\xc5(\x87!\x96\x90\xca\
-\x98k\x94KJ\x8e9\x8b)m\xc4\xb2 \x9b\x8f\x03\
-\xc2os\x93\x96,l\xb7\xa1\x80\x10c\x0c\x05\xaa\x12\
-|\x1b\x0a83\xc7P\xe0\xacY\xd6\xa1 #u\x1d\
-\x0a\x8ac\xacC\x81a\x8c\x91 -\xd7\x1d\xcf`\xa6\
-D9^\xd5`a\x8aZ\xa58\xa4\x92\x86,\xbc@\
-h\x18\xe6\x9e1\x84\x8f\xf7FA\xe0L\xe1\xebL\xe0\
-\x5c\x8a6\xacJG@\x88\x16\xcd\x0d4\x0d<]q\
-,IQ\x10\xcbFl\x08\x04Qv\x92.\x8e\xa8\x00\
-\x16w\x93\x07\xd1%D\xd3\xfa\xe8b\x10\x8a\xee\xb1W\
-\x08\x92\xb9\xc4H\xa7\xa99O\xf5\xc5\xc3\xf0\xa2\x8c[\
-gezl[\xc1m\x1c\xc2\x82L\xca}\x87\xb0\x12\
-iv\xcf!,9\xf2\x9eC\xb8\x17\xf5\x07\x0eA,\
-c\x1cg\x04\xc1\x22\xa5,\x9c\xc1H\x07\x00\xd3\xad\xd0\
-\xe0!\xa2\x12\x98c!2\xc7\x8a)\x10z(\xafE\
-U\xdc\xc8\x87\x92\xae\xea6J\x07*\x8a\x0c\x18){\
-\xe1M\xc5\x0c.\x16T\x9b\xb3\xd9H\x07-\xc8>\x9a\
-\xd8\xcc1\xc8$oF\x0f\x883Qd\xa3g\xc0\xac\
-h:U\xd9\xd6\x99\x03*^\x9c7;\xdd!\x84\x91\
-6\xa1\x04wM.\x0f|F\xc9\xd0\x1f\xf8\x0c\xa6\xe7\
-}\x9f!q\xa5\xa9\xfez\xe04\x82\xea\xdb:M\xe4\
-\x13\x9c\xa6\x10H\xb0\xd9\xdas\xc5\xb4\xf8\x9a\x95\x12%\
-\xd6\x8c\x92\x07\xa5\xef\x15\x06\x11*4bE4T6\
-1\x87\x0cE\x1a\xa2HC\x95\x81KP\xf2v\x03\xac\
-0\xa9\x8dd\x93\xa8\xae]6\x0aI\xe1\xc9v\xfe0\
-\x85\x8e{\xb12C\x86\x14\x1c/J\xb2\x84\xd9\xe8\xcc\
-\x02\x16\xa1J}(\x0aH\xd7(](\x12\x88\x94H\
-\xdb\x080\x81`\xe9B\xde\x87\xa2\x04CB\xe2\x0d4\
-\x1d0L}\x1d\x8a\x0a\xabQ\x1f\x8a\x14\xdd\xa2\xa3\xc4\
-\x0b(\x19\xf3\xd0\xa04\x90 \xb6\xb1\xa8\xb0\x15.C\
-\xc4\xc3\x08\xcc\x1c\xb0d\xe6b{\x9e`R<\xa8a\
-U\xda\x18\xd8\xccs\x13\x15\x87\xa4`-\x83\x1eT\xcd\
-\xe0i\x8a\xa6\xd0\x09\x9f4\xdd\xda'm\xd6'\xbf\xdb\
-\xbf\xfa\xf1\xdd\x9f~\xf7_\xea_?\x7f\x00\x9a\x00\x00\
-\
-\x00\x00\x15,\
-\x1f\
-\x8b\x08\x00\x00\x00\x00\x00\x00\x00\xed=ko\xe3F\x92\
-\xdf\xe7W\xf0\x9c/1N\xa4\xfa\xfd\xf0xf\x81\xdd\
-A\x82\x00\x17\x1c\xb0\x9b`\xf7[@K\x94\xad\x8b,\
-\x1a\x92<\x96\xe7\xd7_U\xf3\xd5$[\x14\xe5\x91'\
-A`;\x89\xa9\xea\xeaWu\xbdY\xad\x5c\xffm\x7f\
-\xbf\x8a>g\x9b\xed2_\x7f\xb8\xa0\x09\xb9\x88\xb2\xf5\
-,\x9f/\xd7\xb7\x1f.~\xfd\xe5\x87\xd8\x5cD\xdb]\
-\xba\x9e\xa7\xab|\x9d}\xb8X\xe7\x17\x7f\xfb\xf8\xee\xfa\
-\xbf\xe28\xfa\xc7&Kw\xd9<zZ\xee\xee\xa2\x9f\
-\xd6\xbfog\xe9C\x16}\x7f\xb7\xdb=\x5cM\xa7O\
-OO\xc9\xb2\x04&\xf9\xe6vz\x19\xc5\xf1\xc7w\xef\
-\xae\xb7\x9fo\xdfEQ\x04\xf3\xae\xb7W\xf3\xd9\x87\x8b\
-\xb2\xc3\xc3\xe3f\xe5\x10\xe7\xb3i\xb6\xca\xee\xb3\xf5n\
-;\xa5\x09\x9d^4\xe8\xb3\x06}\x86\xb3/?g\xb3\
-\xfc\xfe>_o]\xcf\xf5\xf6;\x0fy3_\xd4\xd8\
-\xb8\x9a'\xee\x90\xa8\xb5vJ\xd8\x94\xb1\x180\xe2\xed\
-\xf3z\x97\xee\xe3vWXc\xa8+#\x84L\xa1\xad\
-\xc1\x1c\x87u\xb5_\x01)\x0e.\xc6\xb5\xfa\xb3\x03\xf9\
-\x1f\xe0\xdf\xbaC\x05H\xb6\xf9\xe3f\x96-\xa0g\x96\
-\xac\xb3\xdd\xf4\xd3/\x9f\xea\xc6\x98$\xf3\xdd\xdc\x1b\xa6\
-\xa2~k\xde\xd6\x91\xac\xd3\xfbl\xfb\x90\xce\xb2\xed\xb4\
-\x82\xbb\xfeO\xcb\xf9\xee\x0e\xb8\x81\x19\xf7\xf1.[\xde\
-\xde\xed\x9a\xcf\xcb\xf9\x87\x0b\xd8\x1f\x17\xa4\xf8\x5c\xad\xe0\
-\xaa\xe6#\x92pV\xa0\x96\xc3\xfaM\xc2$4\xdaX\
-\xad\x88Ci1_k\xb8y>\xc3\x15~\xb8xH\
-o\xb3\xf8K\x9e\xdf'0\xed\x97\xf6\xc0\xf9\xe3\xee\xe1\
-q\xf7[\xb6\xdfe\xebb\x1c\xd8\x99\xb7M\xd7\xec\xfa\
-%\xad=\xd6\x03d\xfb\x87|\xb3\x8b\x17\xcbUV\xcc\
-6\xbd\xcb\xef\xb3\xe9\xc3r\x0d\xfb\xde\xe4\xf00\xdbN\
-\xf3\xfd\xf3m\xb6\x8e\x973`\xb5)\xf4[\xa57\xab\
-l\x9a\xcevK\x07\xb8OW\xab\xa90{a\xa6\xdb\
-u\xfa\x10\xc3\x80y\xf2\xb0\xbe\x0d\xce\xb4\x9f?\xc0\xd1\
-R.\x83\xad\xcfu\xebGh\xbe\x9eg\x8b-\xa2\x15\
-T\xc7O\x5cP\xe2\xda\xa0\x15\x18'K7?n\xd2\
-\xf9\x12\xc4\xa5\xc0\xf3\x86\x9c\xe5\xabU6\x83\x93KW\
-O\xe9\xf3\xf6\xa2F\x80\xa1\xda]\xb9\xa5\xaa\x1c\x14\x86\
-\xdd\xee\xf2\x87\x0a\x17\xcec\xf7\xbc\x02\xb2 0\x86\x11\
-\xf3\xcd\xd5w\xf4\x86I\xce\xdf;P\x0e\x0c\xb4\xdc=\
-_\xd1\xf7\x17M\x9f|\xb1\xd8f01\xf1`\x8em\
-\xa0\x07\xcc\x05Je\xfau\xb3\x91\xd0l48\x1b#\
-\xf5l\xd7\xd3\xf6\xb6\xbf\x8e\x8cNn\xaf\xee6\x19\xe8\
-\x99\xef\xfe\xf3\xf3\xff\xfc\xf4\xe97\xfb[\xac\x06\xc8\xcc\
-\x0c5\xa2n\xbf-\xa1\xbf\xae\x97;\xd0$\x8f\xdbl\
-\xf3/\x94\xc6\xff]\xff\xba\xcdzX\xbfl\xd2\xf5\x16\
-D\xff\xfe\xc3\xc5\x0e\x1fW\xa0|\xbfg6\xb1\x8a+\
-i'1\xb0L\xa2\x14\x93\x97\xcd\xfa(\x90DX\x90\
-E\xab\x1a\xd2<\x03\xd4\xc8\x84s!H\xb3\x96=\x03\
-\x5c)p\x04\xe3\xe12\x1f\xb7\xa6\xe2\x01z\xc1v\x03\
-T\x18\xb7K\x5c\xab\x15\x09\x17\xdc\x10\xdeZ+%\x0c\
-\xc0B\xb7\x96jT\x22\xb9\x94\xd6\xb6\x96\x0a\x03H$\
-\x869\xc4\xca!\xb6\x0c0\x9c\x9c\xe1\xef\x00{W\xcc\
-\xa5X,\x0f\xb3r\x85\xa5\x85f\xf1\x91)\xb3\xc5\x82\
-d\x8b1\x12\x95p\xca\xadPz\xdc\xc4$\xa6\xc3\x13\
-\xa7iz\x93\xdaQ\x13k\x06ZB){x\xe2\x90\
- \x86\xe8\x0bG'\xe9\x18\xfa\x0a\x8f\xbe\x7f\x06\xe1=\
-\x95\xad\x03\xc2{\x9f\xee6\xcb\xfd\xf74\xb1\xf8c\xe8\
-\x84\xc0\xaf*>Y\x09r\xccXB8cz\x12+\
-NA -\xb3\xdf@\xa6q\xa1\xe9\xeaU\xc8\xd8\x1e\
-\x1a\xc9h\xceFF\xa0\x95\xfb1\x8e\x8c\xe5'\x0aT\
-\x14\x0a\xf6\xcd)>R\x0d\xbb\xe5\xba!\xe2l\x8fD\
-D\x04\xe6i\x90\xd93\xba8\x04\x87\xa0\x0dt\x11\xc4\
-]\x04q7\x1f.\x90\xb0\xe0\xfb\x1cS\x95'\x91\xb5\
-=\x86\x16\x96\x0ds\xa9%_O\xde\x03&\xc6 \xe3\
-Q\xde\xe5G\x9dP\xaa\xba\xdc\xa8\x8d\xe6\xa6\xcb\x8d&\
-\xd1\xd2\x18\xde\xe2F\x0aS0nGX\x98\xd3\x1d\x1b\
-G\xad\xf1\xae\xc6\xc2\xfd\xbc\xd0\xb1\x81\xb9\xc4I\x8eM\
-h\xb6\xd1\x8e\x0d\xcc\xa6\x8e\xe9\xc6s\x08u\x87\x9e\x94\
-\x9aa\xe1\xf6\xb8s\x1c\xf7\xa14\x1a\x9d\xc8\x96 \x82\
-3c\xb4$\x9e6CA461\x82\x1a.[\x82\
-\x08\xeaPjmmK\x10)\xa0j\xc6H\x7f5}\
-%B@\xfbRI\xb8\x14\x13\x88\xa2('\xe0n\x10\
->)\x1fQ\xb7 \x82\x90\x0a\xb4\x09O,W\x94q\
->\xa1\x14,\x22\x01\xddz9\xc63\x0a\x90q<\xa3\
-\xcc\xf9b6\x8aQ\x82lI=\x0f\xf8\xb0\xafPE\
-w\x9d\x01\x13)A\x8b\x12O\x90\x83+\xbcI\x17r\
-\xd1q\x99H\xc2\x80\x8e \xee\xec\xfd\xa8\xf9Up~\
--\xa5b\xd4;\xc7\xe0\xfc\x86\xdf\xc8\xec\xa6;\xbf\xb0\
-\xe0MZ64\x7f\xd0=\xd1\x8b\x99\xea\x8e\xa5\x8d\x10\
-\x86i3Z>\xc1\x90\xff\x01\xf2\x09+\x1c\x90O\xcd\
-\xbc\x83tfP\x83\xee\x06\xcbF[\xd2'X\xa2\xa4\
-\x96\xac%|}\xd4E\x08\x15d\x8f\xb3DR\xa6U\
-?\xd4\x09\xf9A\x1a\xf8\x8b\x17\xe2f9\x08\x86\xd0N\
-\x0a-D\x02 r\x13\x87`\x15\xe3\xe8\x1aQ0\x14\
-\xda\xc0\xa3\x92\xc0\x98`\xd9/G*\x9bW\xb0,H\
-\xe9\xf1LU\xb8)/\xb5,\xcc\x0c\x88\xf0\xc8\xd9\xc6\
-[\x16f\x8er\xeeb\xb9\xdae\x9b\x9agp\xd6x\
-\xb9\x06\xd0C\x0e^\xc32_\xc7\x05\x06\x9c\xc4\xf6\x9f\
-?\xfe\xfd\xe2$B\x17]a\x19^Nb\x91\xfd\x98\
->n\xb7\xcbt\xfd\xf7\xd5\xe3\xc6[\xf4\xb1\x01\x91>\
-\xf3O\xd9\xe7\xa5[\x16j\x14\xa54\xea_\xdd\xd9z\
-{\x06\x98\xddxD(\x96\xf4jb\x0bq\xaf\x8a\xb9\
-\x17$\x06DW\x04l\xd9\x8b\xa3\x0f\xe7.S\xf4_\
-\x85!\x0a\x9eA\xe4\xc0\x00\x1a\x10.\xd1q\x94-8\
-\x80V\xd3\xb6\xa3lUb\x94\x91T\xb6UD\x0fw\
-\x11\xc4m\xe9\x88\xf3\x8bfM\xcdo\xe4\xf8a\xd6\x22\
-\x96\xf1@`|n\xe7\x0f\x837\x98\xf1hf\xeb[\
-\x88\xa9\xa0\xaf!\xa6\x10\xca\x19\xcb\x055\xc3R*\xe8\
-A)=_b\x80\x92\xdfb3\xc0o\xe8\xf5\x9e\x9a\
-\xd6s\x81\x13X1%\x8c'\x18\x18:q\x9epb\
-\x98\xe7\xe4\xba\xd0\xc9\x9a\x84\x1aF<\x5c\xe6\x1cb%\
-\x8d\x1a\x9f\x9ako\xe5\x84\xdc\x5c{\x8d\xd4K\xd2U\
-\xf98\xa3\xa4\xa7\xa1\xaa|\x5c\x01\xfd\xba|\xdc\x0f\xee\
-'\x94\x1f\xb2\xb1\xfa\xfal\x94\x04\xff\x85\x07F\xd7\xf4\
-x\xf6\xe9\x8f1\x05\xec\xe4 \xeb<\xa6\xe0u\x9d\xc5\
-\xd7L\x9a\x10A\xbdLhX\x84OM\xeem\x1f6\
-Y:\xff9\xdb\xdd\xe5xD\xd9\x02W\xd5\x16p\xc3\
-\x13E\x88l\xe7F\x804\x84C\x10\xd9\x16p\x10\xed\
-\x84\xa3\xe7\xe5K\x10x\xc3\xe8\xae\xbc \xc0,6\xfc\
-\xad2\x1fD\xf0x \xf7\xe1\xe1\xd9X\x86\x06Mh\
-\x91\x00U\xc3\xb2z\xc0h~\xbb\xa4\x0b\x84$@\xd6\
-?\x81\xd5eZ\xbc\x8asL-\xa3\x82(\xd6\xd9|\
-\xd79\xd6\xea\xcf\xe5\x1cs{\xbe\x9c\xf2\x9f\xd29\xfe\
-&\x9e\x1c{%\x9e\x92BA<e\x87yJ\xb0\x83\
-<u~\x83\x10\xe4\xa96\x8a\x12\xe2\x0c\xefj\xeb4\
-\x07a`\x098\xe6\x12\xc1 \x80y4\xca\xb1\x15\x91\
-\x9c0m\x90\xdd$\xd1R\xc9I,T\xc2\x158\xb6\
-\x98qg\x98p\x07\x93\xd9\xc9\xb8\x13\x9aHNE\xcb\
-\xacP\x93(0\x22\x9a\xb6\xccJ\x1f\x15\x80\x12P)\
-\x0c\xcb\xce\xfb\xfe\xa7c\x828e\x83\x22\xeb\xf9\xf6(\
-K\x1a_\x89\x81\x19n\xcb\x92@\x1a1FhK\x96\
-\xfa\xb8\x8b n\xf3Ff\xdcA\xbeB\x9a\x08\xa9\xf0\
-\xad\xcc0\x9e\xf3\xb7\xb3\x85\x9c\xfe\x11/ \x8eq\x15\
-{\xc9\xfb\x87\xd7\xe5\xbd\x93\xbd7n\xac\x8eu\xcc\x0f\
-1N\x9d\x060\xd6\xc6\xa2\xf5\x8a\x7fdH\xb5\x98\xe1\
-o\x87\xcfN\xe3\x1d\x83\xbf\xdd\x11B\xae\x9dd\x0c\x02\
-,\xdd\x8d\xe4\x8b\xea\x1c\x02AV\xac\x8f\xbb\x8e\x80\xc9\
-cv\xc0y\xd4\x8cs\xad\xa5=\xb2g\x92-\x025\
-\x16c\xa6\x96.\xbd\x13\x9a\xdaX\xc2\x84\x1f\xe7\x84\xcb\
-;xF\xb3~\xc5\xc3\x98\xa95\x1cp\xe8\xcd\xc8\x91\
-\x19g\xd9\xecfvs\xe8\x80O\xab\xa5\xe8GK\x1c\
-\xb8\xff\xc8\xd2\x99 /\xe0Kb\x95y\xc9\x19\xc1l\
-\xfct\x22\xa5\xd9l\xd1\x93\x82\xf7\xe7\xa2\x92\x90\xd4\x80\
-\x8f\xa1\x8fQJHFb\x1af\xed#l\x95ep\
-\xce_%\xc5\x02\x7f\xc7H\xb1(\xde)\x84,\x81\xb4\
-\x0a\xd5\x10?~L\xb0U\x16\xdb8\xf4\xa6\xf1\xc8Q\
--\xd4B.\xe4y\xf8\xf9k\xfcFK\x87j\x84\xf0\
-ec\xcbSS\x1c\x8d\x85\x9f\xbaw)>\xacH\x91\
-\xca\x83\xa2\xab\x16\xc0eE\xceD\x8d\xae\x98y\xc5\x1c\
-\xca\xd1\xad{\x19\xcb\xf1\x89\xc5\xd7#\xd0\xa1\xc0\x0e_\
-\x0b\xbb\xd2+\x81Q\x9e\xc1\xfc\x8a!\xfa\xf8K\xfcW\
-%\x9d=\x91tC\xc5\x0c\x8c\x13\xad\x80\x11\xbb\xbb\x8d\
-\xa5L\xa8\xa1\xd0:aHI\xd8\xb6\xb9|\xa5\xc3\xf8\
-#\x89)\xf8k\x11\x93\x96\xd5f-Zb\xda\x0e(\
-a\xe8_\x92\x96\xfa\x9b\xd22.\x13\x9e\xe4/IL\
-I_]\xcay\x8b\x9aVb])\xb5\xafK\xcc\xeb\
-)\xdeKpO\xf5\xdd\x0d\xbcJ1\xff\xbc\xcc\x9e\xde\
-\xd5\xd4\xb8I\xeb\x9d\xe1\x85\x0eg\xd7\x81\x86E\x0cZ\
-6\xdc\xe4\x9by\xb6\xa9\x9a\x94\xfbi5\x95\xa6\xbf\xb9\
--\xe2\x1d\x19\x8eZ\xb7\x93p\xfb\xf6.\x9d\xe7O\x1f\
-.X\xb7\x11\xaf\x97`\xc0\xa6\xa42R\xf4\x9a]\x90\
-\xc8\x13\xad\x89\xe5\xa6\xd7\x88\xeb!\x22\xa1\xa4\xb1Cu\
-\xe3<\x9f=\xe2e\xa6\xf8\xb18\xea\x87}\xaf\xfb\xe3\
-f\x83\x08\xab\xf49\xdb\xb4\xaf\xd64\x97o\x8c\xa9;\
-\x96\xd7s<\xc8\xf6.\x7f*\xe8\x83\x95\x9b\x8fYw\
-\x06l\xf7w\xef\xe1`\xd3\xed\x06\xcf'\xd4\xf1i\xb9\
-\x86\x0equ!\x88\xca\x1eaJ\x8cj\x99\xba\xae/\
-\xeab\x00\x01\x03t-\x1b\x9f\xf1\x15\xa2\xbf\xa2\xc7\xe5\
-<\xdb\x86\xd7\xe4\xda\xe2\x9b\x9b|\x1fn\xcfo\xfe\x0f\
-\x047~Hww0\xc2\x22]m\x0f\xa1\xacs7\
-\x89\x8fR\xb4\xec\xf2U\x06\x227\xcb\x1a\xb7\x14i\xe4\
-\x83M\x8b-\xcb\xa3\x0b\xd2\x1e/\x04\xdd\xae\xf2\x9bt\
-5H\xe3\xfbt\xbf\xbc_~\xc9\xe6\x8d\xd7\xdf\x1e\xc3\
-\xdbq\xa9\xc2\x1a\x92\xc0\xe2*\xf9\xdc=\xe3\x95\xaf\xfd\
-3\xc2Z\xda\x08\x01L\x88\xc6I\xc3\xab_\xcb\xf5\xed\
-\x1e_\xb6\xa3\xa0\xd5\xecT7=\x07\x9a\xb2\xfb\x87\xb2\
-\xb5\x91\xa2(\xfa\xbc\xdc.o\xd0g\xf7v\x08\xb8k\
-\xbc'5\xef@q7%>\xae\x09\xb5\xe46_\xaf\
-\x9eK\xb4J\xa9\xf4u\x89\x83\xdfg\xbbt\x9e\xee\xd2\
-F\xb1T\x10.h\x951\xb9\xde\xcc\x17W\xff\xfc\xf4\
-C\x1d\x8e\xccfW\xff\xce7\xbf7!\x06\x22\xa47\
-\xf9#\xf0l\x1d\x9f\xe1}\xab\xd9\x15\xea\xd9t\xf7q\
-y\x0f\x02\x83\xf7\xf8\xfe{\x7f\xbf\x02\x15W7\xb4\x90\
-\x91\xda\xcd\xa0\xc5\xb0\x9b\xac\xb8\xa7\x17\xbc\xda8\x9f\xdd\
-/\xb1\xd3\xf4_\xbb\xe5j\xf5\x13N\xe2\x85M\xe5\xa0\
-\xcb\xdd*\xfb\xe8\xe6,\x1e\xab]L\xcbmTA\x8f\
-\xb7\xcb\xebiE\x06\xf7\xe9\xb6!\xcf\xadVu\x22{\
-\x17\xaa\xf06\xa0\xd6\xa4\x92LMb\xbcz\x90\xc5\xea\
-\xb2\x22\xe3m\xcdU\x01\x0b\xc4\x85`\x8a\xa1\x01\xa7\x09\
-3`\xb3\xb1X\x8fY\xc9a\xc2\x89k\x96V\x13\xbc\
-\xd2@t\x02\xce&\x07\xbfS\xf2D\x12a\xd9e\x9b\
-5\xb1\xd4\xb7\x09\x937 \x82\xbd`\xb0\xa9\xc4\xb4\x9a\
-\x12\x0a\x91\xe4\xfb\x05P\xf0\x0ah\xfb}\xc7\x10\xbbk\
-V\x97\xae\xd9\x0b\xeb\xb7\xbbM\xfe{v\xb5\xce\xd7\x0d\
-+\x96)D\x98O\x0b\xe5\x07\xdb\xa5\xc6\xeb$\x8f\x1b\
-u\x8c\xf5\x0e\xae,\xd1ks\xaf\xbd-P\x80\xb7J\
-Y\x81\xafc)\x12\x10=\xdd\x1a*DRM`o\
-\x10\x8fL\xbc\xc78\x0c\x06\xa3\x7f\xe9\x07\xdcm\xa2\xe1\
-\xa4B\x83\xec\xd2Vi\x83[\xa2J\x14k\xec\xaa\xbf\
-+\xeen\xa5\xb0V*\xa5\xd2\xfdX7\xa7x\xef%\
-NA;\x1d(\x0f\x1e:\x1d5\xf2t\xceL#\xb4\
-\x09\xde:+\x05S\xa8\xcct3\x1b\xdaD\xaf\xd0\xdd\
-\x98\x13X\x0cg\xd6\xc2\xb4\xf29\xd5\xf4\xeeE\xa2D\
-\xd2\xb7\xd8\xa9iw\x97M\x9c\x226\xa1\xf6\xcd>\xc0\
-\xa8M\xebs\xa0\x15\xb5fD\xa5\xc6\xd0EL\xaa\xc1\
-\xa3Y\x84wh\xac\x90BF\xa0\x0b\xb8\x01\xc50)\
-:Gq\xf1\xb7\xfeX\xe0\x81\xa0\xd7-U\x8f\x06P\
-\xe2\xc21\xd4\xff\xae\xa2\xb2\x91D_B\xcb\xdd\xee\xd2\
-M7\xf1U\xb5ekX7O\xa8\xa0\xd22}>\
-6\xa1\xd4\x82\xefF,x\xcc \x1c\x9aI+\xed\x80\
-l\xbdH!Yr\xb2B\xb2\x01\x85\xe4\x0eJ\x9a\x83\
-\xb2\xeb\xbf\x96\x18\xd4H\x01\xe5pNa\xbb\x9e\xde\x96\
-\x0f\xbe\xcc\xf5g\xa0\x89\xd4\x960\xa5'\x18\xd1X\xc6\
-\x85b8A\xfd\xdc \xc4\xb0\x13\x89\xaf-\xc1\xe0p\
-\x99H\xce\x98\x97\xbe(8\x1a\x0c\x0d\xd7\x10\x13\xd5\xd7\
-G\x1cKS<\x22\x09\xa1e\x14\x9b\xc4\x80M2`\
-\xa9\xaa\xfb!Q\x5c>\xb6@U\x0fd\xef\x1a>\xa9\
-\xba\x9b\x16\xb0\xe9G&M\xcf\xa8\xc2\xf5\x11\x82\xbd\xfc\
-\xc9\xaa\xb5\xd4\x13\xf5\x17\xd7\x88MK\xc6\xfb\x17^Z\
-\x0ab\xa09|\xdf\xa6\xa5\x9eZ\xf7sj}\xe6_\
-\xfa:\xae/\xc1\xe8\x8f\x91\x80\x80V\xae\x99j\xc0\x1d\
-\x81xPIK\x84\xbbG\xa0(\xb8\x18\xcaqj\xfd\
-\xdc \xc4\x94\xe3=\x02\xc5\xc05Q*1\x10*\x8b\
-\x9eCB\x14V*\x8d\xb3\x1cM\x5c\xe1r\xd9\xbe0\
-W-H\x86G\x885b|\x1d&\xc0\x15b\x22\x84\
-\x96o\x96\xb7\xcb5F\x0a?G\x10\xc4\xe3}h\xd0\
-\xa7\xael\x0e\x96\x1b\xfd#\xa2\x10\x90+\x108\xd3\x00\
-\xdduP\x87'Ub\x0d\x07y\xf1`Z4}k\
-\xa0\xc5\xb2\x02c\x19\xf5\xc6\x03!\xae\x06\xac'n`\
-\xd0\xdb\xda\x04\x5c<\xc1<(\xa3<\xe1\x94\xf9\x03\xd6\
- o\xe2\x1a\xe6-\xb0\x1e\xad\xd9G\x7f\xbf_\xa2\x16\
-\x19d\xf9\x80\x8b\x01\x86\x84\xe7\x06\xc6\xd0Aq\x9d\xa4\
-I\x84T\x1c$\xa5\x81\xb5\x16S\x01-I\x0c*\x14\
-\xd3\x0c\x07Q|k\xfb\xc2\x91\x5c\x82\xc9\xe1\xa6h\x94\
-\xcc\xef\x07\x9f|\x02\xc3\xc7f\xf6\xba_\xbd\xc6\xc0V\
-\xbeD\xe3}\x0e\xbc\xbc3$B\xef\x8b\x9a\x99\xa2k\
-s\x87\xe22\xe4\x8a`\x01W\xd7\x1d\xa8V7\x11`\
-cA\x1b\x80\xee\x8c\xf1\xee\xac\xb1\x85\x99\x07\xa9\x11\x0a\
-\xe0\x13*\x12\xa2-'\xca\x83U\x0f\xa8n\xcb>@\
-\xad\x02\x916\xad}|o\x06\x84\xe1\xfc\xb1\xebg\x14\
-\xa8\xbf\x1a\xe4\x8d\xef\x16e\x09Q\xa8\xa8\x01\xd3\x15i\
-5\x08q\xbf\xcf\x97\xe8\x1e\xd7\x95\xe01\xcd\xe0`\x12\
-\xbc\xc9\x05\x9a\x22bx\xa1\x10q\x91C\x15%\xc8\xd3\
-\x15\xa8z\xc0\x9eE\x0fh\x8d\x1d\x22\xe1E3\xce\xd6\
-\xc2G\x80C\xe6\x8eb\x05h\xe2:Y\xaa\x08m@\
-%6\xee\xc6\xe1s\x0bB\x81h\xee\xe6w=\xb7\x8f\
-^0\xcc\x11\x83\x0d\xd3P\x8dQW\x91\x16\xac?R\
-\x89%\x96\x92\xd1\x89J\x984\xc4\xdd*?\xe8\x1a\x87\
-F6TS\xa6\xa9-G\xae?r\xe0\x16\xd8\x1a\x9c\
-\x01\xac\x1f\xc2Hk\xf5\xe5\x00oA\x88iy\xcd\x5c\
-\x16\xdc\x1c\xed\x98\x8b%(E\xc8[\x12v\xccE\x03\
-*\xff\x16\x9c\x85\x1d\x94\x8a*\xb4\xba\xb1\x8b\xec\x8d\x8d\
- T?\x8e\xaf\xa4\x01K\xc4=X3z\xb9\x1e0\
-\x07\x8e\xaf\xf0\xde\xa1i\xda\xe3~\x97\x82\xad8\x90\xa2\
-\xe0+\xb0\xa1\xd6P\xc7Wx\xcf\x19\xfcjP\x14\x10\
-\xec\x98\xf2\xb3\xfb/n\xa3DD\xfeE\x14p8]\
-\x9b\x1b'np\x8b\xcf\x0e[\x0b\xea\x18\xaa\x84\xba\xca\
-0c\xb8\x01\x9d\xe5\x01\xebN\xb8\x99\xb2\x1b\x8f\x0aT\
-\xca\x8av\x19\xb5\xf1\xdb,\xd5\xc4*$\xf0\xee\xb9q\
-y\xa5 \xa0\xc9\x15}\x7fPY\x092h\xefC\xca\
-J\xd0\xb6\xb2z\xb3\x8aoV1\xe0C\xe1\x9dd0\
-*\x96\x04\xa3\xe1\xb0KvP\xd3\x1dSO\x9a\xf5\xb4\
-\x93\xe4\x07u\x93fQ\x892\xac\x98\xdc\xa8\x1d\xbd$\
-\xad\x12\x03zII\xaeJ\xbd$\x88<Y/\x81\x8a\
-i\xa9%\x94\xdf\x90Z\x02\x17\xb9RK`\xf2\x8e\xa9\
-%\xd0n\x01\xb5\xa4\x88f\x83jIRm\x0a\xb5$\
-\xb9<I-\xb1V\xec{<\xd3dG\xe7\x01\xdf\x94\
-\xcd\x9b\xb2\x09+\x1b\xc5\x05\x16l\x9fA\xd9\x04\xc3V\
-N\xc1?\xe3\xd5;\xf1\xf2S,\x13L\x00Ye]\
-ZMi\xa2\xb9\x1cr\xab\xe0xk\xad\xc5 \xa4\xa5\
-\xa5\xc7\xce\x9cHQ\xf7\x1a\x97\x09\xee\xc1\xaa\x87Bw\
-a\x1fp|*\xc4\xa6\xb5\x8f\xef\xcd\x80\xb0\xc2\xbf\x05\
-\xaf\x8f\xa3_U\x00\xbc\xb1\xcb\x05\x19\xa9\x9d\xf3\x0c\xce\
-\x9b\xb5\x1eB\xdc\xedQ\xf9\xea\x95\xab.\x0dj.\x8e\
-70\xca\x9d\x08\xc1\x98T\x0d\xa8z(\x1cup\xf0\
-\xac\x15n.\x01\xde\x0fw\xcd\x85\xa2\xf4;8H1\
-\xbc-T|\x01t\x9b\x11\xa0g@B\x1aX\xd5\xa3\
-p\xd7]\x1f\x1a9D^\xb6\xba\x05\xb4\xf0\x1d$\xac\
-\xc4\xec\xb0oE-t5\xe2\xb0o\xc5F\xe6R\xde\
-\xd4\xda\x9bZ\x0b\xab5\xcc\xe12b[\xdf\x07rN\
-'\xca\xaa&\x81\x00Q\x18\xd1\xa5:\xaa\xf2\x07\xe0\xbd\
-\x09\xa6\x1aP\xf9\xb7\xd0E\xae\x83\xcb\x1eP\x97\xb0\xab\
-\x1a\xbb\xc8\xde\xd8\x08\x92\xaa\xf4\xa5\xa0\x17t\xf3`\xcd\
-\xe8\xe5z\x84,b<*-5M{\xdc\xefR\xa8\
-#V\x88\xbfSH\xb0C\xa6\x8b\xdc\x81*\x14\x12\x83\
-\x11!`-\x01\xc5\x9fB\x19!\xaeRN\x19\x01\x12\
-\xa7\xae\xb1p\xdb<lY)#@W\xb6L\x1cT\
-\xdb\x81\x9e\x86H\x1c\xa4\x01\xd6\x9d\x0a}\x84\xdd\x8c\x88\
-\x0aTk&\xe5\xd8Q\xbb\xc3Am$O{yg\
-\x07_\xde\x05\x93PZ\xbc\xc5uo:i\x84N\xe2\
-L\x18\xabZE\xe6/\xd5I_\x91\xa3\x077\x0c\xe4\
-\xd9\xb4^\x91\xbf1\xed\x1b\xd3\x86\xd4c\xff\x1e\xf2\xc9\
-Y/&\x82)zn\x87S\xf4\xda\xcb\xd1\x13\x0b\x82\
-Sz\xb1\xb6\xccg\x1b!\x09\xd5\x0d\xa8\xfc[\x98X\
-\xd7\x81G%\x1a\xab\x1b\xbb\xc8\xde\xd8\x08\xaa=d\x97\
-\x8c\xa6\x1e\xac\x19\xbdX\x0f\x87\xf0\xc5\xd9=C\xb9\xd6\
-M{\xdc\xefR\x98XR\xe4.\x9c\x89\xd5\xda\x15\xb6\
-\xe2\xf0\xe5f\xa4\x95\xda\xc8\x0aP\xfc)L,\xe2\xd2\
-\x221/\xf15\x02\xc7\xab*\x95\xbf_\x22\x8b\xda\xdd\
-\xd7\xeeB\xb7k\x13\xf5f\xe0\xaf\x92\xc2\x07\xd6}\x0a\
-\x03\x0b\xbd\xa4U\x91C\xc5o\x06,\x87\x8eZ\x1dF\
-e\xe7%\x97\xcc\xa8\xaah\xb7\xfe\x08\x1c\xc9\x94\xb4\x1c\
-\x0e\x22\x11\xca\x0a\xad\xcd\xb1w\xe9My\x5c>\xcfP\
-\xc7\x81\xe6\x9am\xb7\xdbf\x09\xcd{Z\xdd\xbc\xbcu\
-|$q\xdd\xd2\xc2\xb9P\x86R\x05\x8bp\x8c\xc4A\
-\xf1I|w\x817\x93\xf1\x8b\x94\xc1\xe9\xd0x1Y\
-BDH\xf1\xd5\xa5\xa4R\x15@+1N\xa5\xa8\xaf\
-\x8cf\x10Xb\xc8\xc3\xb8\x9d\xa8DP-\x84F\xff\
-M\x80<\xc0,\x16B1x\x8c\x98\x04v\xc0\xea\x0d\
-\x80 sB\x9c\x86\xdf.\x0aS\xe1\xfb\x7f\x98\x07\x5c\
-Bf\xe0\x1c\x89A*\xd3\xc40\xae\x15G\x98b\xc4\
-h\x80a%\x19\x84\x7f\x1cdY\xc33\x9c8,\x97\
-\x09\xbcH\x8dN\x9d\x02\xd7S\xe2\xeb}\xd0O\x04\x1c\
-8\x15\xdc\xab\xf7\x0a}\xd8\xdd\xc1;\xe0\xa3\x22-\xaf\
-n|\xbd\xcef\xbb|\x13\xcf\x1e7\x9f\xd3\xdd\xe3&\
-\xc3j\x96\xfa,\x8f\x96C\x94\x89\x00\xe9\x8a\x1c\xac\xb5\
-\x04\xc3eW\xa0\xa35a\x9d\x82\x07\x8dE\xdb\x94\x03\
-J}\xff\xd4+\xe2\xf1\xaax\x8e\x95\xf1\xc8@\x1dO\
-\xa8\x90\xa7\xc6\x8f\xea\x81\xab\xc6\x0e\xae7p\xd9R\x0f\
-\xdb^\xc2\x81z\x86N\xcd\xd2P\xb5S\xab\x92\xa1\x7f\
-\x11\xb7U\xc9\xd0\xbf\xbd\xdb(\x5cJ\xba|\xe1\x05\xe5\
-L\x00[\xd2\x81\x17\x1e\x9c\x8e+\x08\x1b*p\x08\x0a\
-x\xbf<\xed%\xabc\xe3R\x06\x0d5\x18\x19K\xc1\
-\xb1\xf4\x1f:\xba\xc0\x91\xff\x059<X\x7f\x8b_\xf8\
- \xc1\x0c\x1b\x8b)xP\xffL\xd5\xd7\xd1J\xdd\xdf\
-\xaa\xe7\xe5\xb6\xfe\xaa\xd6\x1e#@$JE\xf3\xc5\x89\
-\xad\xca5<\x1c\x5c\x91\x17\xef\xed\xcb\xbb\x1c\xde7\x14\
-U\xf5dE\xa5\x99\xf5\xbe9\xbc,A\xe3xC\x07\
-\x1c\xc4\xce\xf7\x8c\xc2D\x9c\x8a^AP\xb34C\xdd\
-/'\xd5O\xc1\xac\xdf-\x88\xfb\x12\x82\x01\xdel\xbe\
-\xa4\xc2\xdb\x8dGJ\xfc\x1fr\x00\x19\xe9\xc4\xbb5\xd4\
-\xa7\x8cbF\x02qN\x9f\xbe\xb7M\xeb\x15\xef\xb4\xea\
-\xf2\xacG\x94\xfa\xff\x9aR\x14\xcez-{,\xbf\xeb\
-P\xfd\xd9\xbf\x80\x13\xdc\xefs\xf0\x8a\x0e\x0c\xa5\xf1k\
-e\x8c\x7f3\xf7\xf0\xd4\x07W\xeb\xedm\xe0\x08K\x1a\
-\xb2\x8a\x84\xf4d\x1a\x1e8\xb5!f\xafk\x14{\xcb\
-\xd1\xc6\x9d(?\xc0\xecAr\x1dbw\x8d\xb6\x16\xa8\
-\xc8Nb\xf7\x01\x83q\x88\xdd\xf9Bff\xde\xa6\x15\
-l\xc5\x82\xc2U\xaa\xfc\x16,\xf89.\x00cg\x0c\
-\xb85Xe2\xda\x1a\x94[\xed\xb1|\x9f0\x15-\
-\x9d\xda\xa3\xbe\xea\x08\x10\xfe\xe5\x1c\x7fN\x8de9?\
-\xf9\x08\x83\x04\xe5r\x90\xa0\xc7TX\x15!LP\x1e\
-\x8a\xe8`@\x97\x9d\xb40{\xdaICH\xd6\xa5\x9f\
-\x93\x0d\x0d\xff\x8c \xb9Sn\x1d+\xf3\xdc\x9c\xd9\xa1\
-\x83\xee\x9eih\x98\x16{\xf5\xe4\xb4\xbf\xc8fK\x82\
-\x9e\x87\x96\x82\x8f\xa2\xe5\xb1s=\x85\x19\xe2\xf3r\x83\
-\xd0'r\x83w_\xec(7t\x0e\xe6\x00+\xc4\x95\
-Kg\x0er\x83\x872\x86!\x0e\x0a\xfe O\xd8\xf3\
-PT\xd2\xaf\xe1\x89\xb8\xc3\x14\xce\x04^\xe3\xb5\xb3\x8f\
-\xef\xfe\x1f2\x91g\xd1\x9do\x00\x00\
-\x00\x00\x10\xb4\
-\x1f\
-\x8b\x08\x08\xd3\x9cBb\x00\x03Gnome-f\
-s-directory.svg\x00\
-\xd5Z\xedn\x1bG\x96\xfd\xef\xa7h\xd0X$A\xc8\
-f}\x7fp$\x0f\x12\x09\xde\x0d`\xef\x06\x1b\x07\xb3\
-\x98?\x03\x9al\xda\x84)R\xa0([\xca\xab\xed\x8f\
-}\xa4}\x85=\xe7\x16I\x91R\xb7V\x1e\xc7\xd9Y\
-F1\xbb\xba\xabnU\x9d\xba\x1f\xe7\xde\xe6\xc9\x9fo\
-.\x16\xd5\xc7f}5_-O{\xbaV\xbd\xaaY\
-NV\xd3\xf9\xf2\xddi\xef\xd77/\x07\xa9W]m\
-\xc6\xcb\xe9x\xb1Z6\xa7\xbd\xe5\xaa\xf7\xe7\x17\xcfN\
-\xae>\xbe\xab0ry5\xba\x19_\xbe~}\xda{\
-\xbf\xd9\x5c\x8e\x86\xc3\xe5U=\x9e\xae\xde6\xf5du\
-1\xc4\xa3!$\x0e/.\x86\xbd\xbb\xde\x8f\xf7\xddu\
-\xbc\x9c\xce\xda;\xe2\x01:\xda;\x89\xa7=y8\xc2\
-\xf5E\xb3\x19\xef\x1fL'{\x01\x97\xd7\xebE\xbdZ\
-\xbf\x1bN'\xc3f\xd1\x5c4\xcb\xcd\x15\x84\xe8}\xdf\
-\xc9]\xdf\xc9\xba\x19o\xe6\x1f\x1b\xccu\xb1\xc2\xcc\x1c\
-\xb6\xbcz\xbe\xeb\xb9>X\xd7\xa7O\x9f\xeaOVz\
-\xe8\x9c\xf3P\x99\xa11\x03\xf4\x18\x5c\xdd.7\xe3\x9b\
-\xc1\xc18\x00\xd66\xce(\xa5\x86x\xb6\xed\xf6\x84.\
-\xa3\x9b\xc5|\xf9\xa1s\x0d\xf2\xb4W}\x9aO7\xef\
-O{.]nz\xd5\xfbf\xfe\xee\xfdf\xd7\xfa8\
-o>\xfd\xb8\x02l\xaaR\x95K\xf8\xe3\x09\xdf.p\
-\xb8+(\xc2l\xb1\xfa4\xfa8\xbf\x9a\xbf]4\x7f\
-j\x96c|\x0d\xde\x8e'\x1f\xde\xadW\xd7\xcb\xe9h\
-\xd9|\xaa\x0e\x06\xce\xa7\xa7=\xac\xcd\xf3\xfa@\x8bt\
-\xef\xc5\x09\x17\xb2\xdbU\xaf\xda\xdc^b\x82Ms\xb3\
-\x19N\xae\xaez\xd5\xbaY`$\xa7\xbdz\xdf4\x9b\
-\x22i:^\x7f\x18\x5c\xac\xa6\xcd`r}\xb5Y]\
-\x0cd3\xc3/\x94\xf5\xaeY6\xeb\xf1b/Lz\
-\xdeI[\x8c\xa9\xea\xcd\xf2\xa1\xdc\xd6%\xc9\xe8/\x10\
-\xb3\x14\xed\xda\x8byVU'\xd3fvUz\xe1\x22\
-\x04\xd7\xe3]\xdc_\x8f\xa7\xf3\xf1\xe2\x9f\xf9\x05\x8d\xad\
-\xdem/~]\xce7\x98\xf2\xfa\xaaY\xffr9\x9e\
-4\xff\xb6\xfc\xf5\xaa\xe9\xed\x1f\xbfY\x8f\x97W\xb3\xd5\
-\xfa\xe2\xb4w1\xde\xac\xe77\xdf\xaa\xdaF\x97\xfb\x8a\
-\xff\xe1\xd2\xf7\xb5\xabs\xf6\xb9\xaf\xfdw\xbdjv\x0b\
-\xcd\x88\xb5\xca\x1ag8\x83^\xb8\x5cg\xe5,p=\
-\xedYU\xabdC\xaf\x9a\x1c\xf6\x9a\x1c\xf6\xe2\xc2\xff\
-\xe3\xf5\xab\x9f\xce\xff\xa6\xfd\xdf\xb6K\xc7\xe2\x01\xd6e\
-Q\x0f\x5cx\xef\xf7J\xc6\xf6`\xb2Z\xac\xd6\xa3\xe7\
-\xc9\x9f\xfd\xf0\xf2e\xafZ\xcdfW\x0d4T\x15D\
-\xda\x04\x84V\x01J\xd9\xf3\x1f\xf2\x9d\x00\xbd\x13p2\
-<Fo{\x17:\xd0\x8c\xd7{Ho\x0d6\xa2\xea\
-l\x02\xc4\xdf\xa0am\x1d\x95\xd7\xbd\xeaV\x9f\xf6\x0c\
-\xb6h\x0c\xa4\xdf\xe8\x83'\xff\xcb1\x1c\xc0\x11:\xe1\
-\x08\xa6u7\xd6\xe6\xfc48\x82}\x92\x80\x038\x8e\
-w\xde\x0d\x076\xedc2[8B\x9dB\xde\xa2\xe1\
-j\xaf\xa3.h8[\x07\x1d\xcd\xe7\xa0\x11\xbb\xd1\xc8\
-_\x88FT\x1d\xca\xc1\xcf\xdf\x8d\xc6c\x06\x95}\xb4\
-0&\x93r\x7fP\xbe\xb6\xf7`Z\xd1Z\xd3\x0f\xb6\
-\xce:\xd0\xbe\x08\xea\x80\xa6\x93\xa2+\xa8j_\x07o\
-m\x81\xf5\xe0\x11[\xda\xd4^}\x16\xae\xa9\x13\xd7\x98\
-\xda\x8dN>O\xc2\xb5\xfd`\xbc|\xfe!p5\x88\
-5N\xdb\x82\xab\x89\xb57z\x87\xab7\xe8\x99\xb6\xd6\
-;p\xb5\x11\xa3y:\xae\xb9\x13\xd7\xd4\xee\x8b^\xca\
-\xe7)\xb8\xa6\xd8* \xe7\xb3\xb3\xcf\xb1\xde?.>\
-\xd8\x88\x1bA\x97\xf8\xa0Cm\xbd\x0e\x12\x1f\x10\x05\x9c\
-f\xf0\x9f\x1c\xf6\x9a\x1c\xf6\xba\x83\xd4\xa8NH\xb3\xfb\
-\xc2\xf8\x90\xdb\x03\xcc\xe7\xc6\x87\xdf\x13R\xed\xa8\xc3\x88\
-\x19\xa6/_Z\xd0\xd5\x80\xd7\xd6\x088\xa1\xaf\xc5\xf2\
-\xed\x16c\x9dj\xc5\xb8@\x88m\xaauH\x82p\x86\
-\x19\x04\xfa\xe3\xc9A\x9f\xc9A\x9f\x03|u\x17\xbeA\
-\xb5\x07\x9c\xfb\x1a\xd7\x89oPO\x0b8\xaa\xf6\xdd\x22\
-\xda\xcf\xd8\xda\x10:\xb4\xfe)!\xfcQo\x92U\xe4\
-\x19h\x1b\x5c\x7f\xffU\xee:\x040\xeb\xfa\xbe\xf6!\
-\xc4\xad;\xd1\x1a\x01\x9f\xc6Io\x92\xeb\x10L(\xce\
-\x84\x06a\xb2)\xbed\xf7\xe0\xc9\xae\xc4\x98\xces\xd1\
-_H\x04\x82\xee8\x97\x1f\xf3\xf9\xf1\xb9(\xefB\xb7\
-\x94\xf6\xa3\x09\xeeG},\xc5:\x1b\xbb\xa5t\x90<\
-}\xa6\x8e\xa5\x04\x15\xbb\xd5D\xb7{\xd7l\xcf\xeei\
-Z\xb2\x8f\x08\xf9}<\xecW\xd45\xeb\x8a)\xdf\x14\
-\xce\x85\xe3\xd9\xb2Nx\x87h}*\xba\x86\x86\x93\xd3\
-y\xba\xb2\xd9Ne3\xed\xba\xf2\xe4\xb8\x15L\x87\x9a\
-\x84\x1f\xed\x17\xb2N\xe7jg\xdd\x16\x0e\x04\x15\x13\xf7\
-p0\xcf\x08\xb1\xc0\x01\x0e\x1a]\xf8,\xdbs\x9dp\
-\xd8v\xdb{:\x1c\xb6\x1d\xcf/T\xb2\xdb\x92\x86\x84\
-`\xd3\x16\x0e\x0bJ\xb8c\xe1\xf0D)\xee\xd8\x22c\
-l\xd4\x9f\x93\x93\x98\xce\x14-\xd8/d\xe1\xc1u\xb1\
-\xf0\x10\xce\xce\xbe,'\x09\xc8\xbd\xa8\x107%_\xf3\
-\x8a\xd4V\x92\x92\xfd\x13\xc2aj\xa7\xec\xe7\x90<\xd3\
-\x99\x94\x04\xd7\xeeA\x9eL\x9e\x83kg\xdf_H\x9e\
-oKV\x16\x5c\x8e\xfb\x14M;xB\x81\x03\xd8\x04\
-\x13\xb7\x9c\xd7\xe9\xda;\x97?\x07\x8e\xce\x5c\x22t$\
-\xf0g\xfe\xa5z\x9avt$\xf0\xfa<\xba\xf3v\x82\
-\xf6\x10\x8e\x93!+$r\xc5R\xdft\xbc\x19\xcb\x04\
-\xbb\x86\xc7n\xb7\xa3o\xb0\xc1\x0f\x0dF\xbdm\xde\xcd\
-\x97\xa7\xdf\xfc\xf7\x7f\xfe\xd77\xec\xfb\xcd_\xfck\xf5\
-\xfa\xf2\xacy?\xff\x97\xdf\xd6\xcd/\xbf\xfd\xeb\x9b\xc9\
-o\x1f&y\xfa\xcd\xc9p?\xa6H\x18\xdd\x5c\x5cR\
-r%W\x9b\x0fD\xe9\xe7j\xb3Z->\xcc7\x95\
-\xad\xd5\xc0\xe4~5[\x8f/\x9aO\xab\xf5\x87J\xd7\
-\xe1\x0e\xbd\xdd\x92v7\xc8-\xa7\xb3\xd1\xbf\x9f\xbf\xbc\
-\xbb\xb3\xbdw\xde\x5cM\xd6\xf3\xcb\xcd|\xb5\xac\xd8\x1e\
-\xbf]]\x03\x89\xeb\xeb\xf9t\xa4\xa7\xde\x197\x9e\x0d\
-\xdeNfa\xe0&6\x0f\x92J\xb3\x81\x091\x1b\x03\
-\xcd\x9fNL\xefP\x22d^B\xc6\xcf\xeb\xd5\xf4z\
-\xd2\xac_<\xfb\x81\xc5\xd1\xea\xe7\xf3\x97\xd5b\xfev\
-=^\xdfV\xbeV\xeadx\xdc\xeb`\xf8\xf0\xde\x9a\
-~\xff\xf5\x0e\xff_\x88\xbc\x87\xea\xcd\xf8rt\xc6\xda\
-ps\x8e\xff_<3H-\x06\xf8\xd3\xea\x8d2#\
-\xfc\xe9\xf0=/\x00\xed\xfd\xae\x0f\xe4\xbc^M\xe7\xb3\
-\xdb\xbd\x1cG9&\xbfQi\xe4\xf3\xc8\xc5\xbf\x16\x11\
-\x87\xbd\xda\x97\xb2Z\xbf\x816\xee\xce\xf8\xa7\xc5\xe2\xfa\
-j\xb3\xe6\xedJ\x83\xf0\x1d,d\xd7\xf1\xe1J\xb6j\
-z\xb8\x16=\xd0\xe9\x8dN#\xe7F\xda|\xaf\xf4~\
-O\xc7\x9d\xffP\x95y\xb8\xf4\xd7\xafG\xe7\xab\xc95\
-k\xf9?\x9d\xbfx&\xa2&j2V\xd3\x94\x06S\
-;U\x037m\xdc \xa5\xd9\xdb\x81\x8fa\x12\xa6!\
-+\xedg\xb2\x95{\x83\xff/\xb72\x9d\x8c\xc8$\xc7\
-p;\xf3\x8b\xf1\xbb\x86\xa5\xfe\xefo.\x16\xf0uw\
-O\xee\x8f\xd8\xcc7\x8b{Z\xb1]\xde\x0f\x8b\xcd\xfd\
-\xfb\xdb'\x8b9\x8b\xd5\xa3R\xa7\xbe\x19\xc0\x93\x8e\xaf\
-\x17\x1b,g\xb6ZL\x9b\xf5@\xd7\xe3y\xd9\xfeb\
-\xfe@\xf4\xb0U\xb6\xac\xf1\xc1Z\x1e\xc7p2\x19\xfd\
-\x85\xde\xf2\x00\xbbnH>\x03\x91\xdb\xcbFd\xae\x9b\
-\xab\xd5\xf5z\xd2\xb4\xbe\xfc\x99N.\xe6\xec9\xfce\
-3_,~\xa2\xf4c\xd7q\x00\xef\xb1K\x19n\xd7\
-}\xe0\xcf\x87\xf7\x1c\xfa\xc9\xf0\xd8\xe9C\xd3v!\xe4\
-~Pj\x96\x88D\x9f\x8eC\xce\xf1\xf0\x93\xcb\xf1\xe6\
-\xbd\x047^x\x16\x9eq\xfd\xbar\x11\x09\x81\xef\x93\
-\x08'[\x9d\xb1m\xd9\xf2\x99\x97\xca%6\x5c6\x95\
-\x033\xca\xd2\xd1\x9bP\xbd\xaa@\x0d\xac\xc5S_\xdb\
-\x14+\x9b\xea\x94\xa4\xd0\x03)\x93\x8au\x0a+\xe5\x0a\
-\xe3\x22[\x1as\xe0\xcb\x85\xcc\x96\x0d\x96\xad\xa0\xa5\xa5\
-s\x92D\xc7\xb3\xe1\x9cd=F\x07\xb6B\x96\x8e:\
-\x99J\xf5U50`d.\xf4-\xdf\x1b\xa0\xe5 \
-7\xa3eM\x92)\x95T\xf9Tp\xd22\xa5\xe6\xa7\
-\xf5\xc3\x96\xf1\xa6\x0f\x05M\xd6V\xfc\x8aX\xaa\xad5\
-\xa5\xb0\x0e\x8d\xa5\x1al'W\x8bj\x80\x1c+\xb1\xd0\
-\x12\xb1r\xd9W\xd2,(jm8\x12I\x16\x1a0\
-R,\x06`\xe5\xbe\xc6\x0c\xa6\xec\x83\xe9\x9aOe\x1b\
-\xc9\xe2\x895z\xd7@g\xc8cyL\x03h\xac\xae\
-\x14st\xe2\xb5e}\xc7\xc5\x88\xe9\xb1(\xaf\xfb\xd8\
-u\x8a\x06\xd3\xb3c.\xc9 \xafC`i3x\x5c\
-k'\xc5\xa0\x0c\xd2\xa0\x95\xc3d\x11\xd8\x02,c\xfa\
-,\x0e\xe9\x8ae\xce\xd8\xc7\xe1\xe1|0Hy\x8aI\
-\xba\xe2LY\xc48\x5cK\xd9\xae6F\xe6\xe6\x8e<\
-\x93M\xa7\xadLn\x00}9\xd9J\x8eR\x8eF\xc6\
-\x05-Gh\x83\x915\xcaYc\xa9l\x18[\xc6H\
-\xc3\x16\x01\xb2g\x9dd\x8cJl\xec\x94\x85\xd7N\x19\
-\xd1\x15\xcfU@\xa1(\x1b\x99\x09\x92\xb6\x02A\xf4\xa5\
-o\xd8\xe2\x81\x86\x17duY\x1f\x08|\xf5\xdb\xdd;\
-HX\x04lt\x84\x11\xdb\xd7c\xe0\xab\xfbW\x98Z\
-\xa9\x7f\xda\xbf\xdd,\x8d\xcd]2.\x97\x0bD\xa7o\
-\xcb\xe2d\x8f\x91EL\x1a\x12\xe4x\xcf\x14\x93/\x02\
-G\xef\xd7\xcd\xec\xb4\xf7|o]\xb7$\xaf\xd5\xcd\x9e\
-\xc2\xfe\xbd\xd3\x22I\xee\x8b\xda\x99\xef\xb6\xe2v\xa2[\
-\xa7\xdd/\xccn\xa7=\xb6\xfb\xa0vv\xefa\xce\xb1\
-o,\xad\xb82\x1a\x87\x0c\xe3\x0e\xb4{Qs\x9b\xad\
-T\x19E\x99C\xa4\xa6\x89U!\x0d\xa15\xe4\x80\xb3\
-Q5,WC\xb3T\xd1\x0f\xda\x18\x8fO\x0e4Q\
-\x8b#\xf5\x01\xb1\xaaOS\xe6\x10\x9d\xa0\x81b{*\
-%\x19d\xb7eM\xf6\xf4\xa9\xa8\x81\xa5^\x86r\xce\
-\x1e6z\xac\x06i\xab\x06\xb6\xa8\x81;P\x03\x95\x8a\
-\x1e\xc4\xa2\x07\xa6\xb8\x1e\xd1\x0a\x9d\xad\xd8e\xd1T\x93\
-r\xd9\x984|8P\x98\x19<\xf9\x08>\xfe\xdb\xe7\
-\xfb\xb7\x90\xdf\xb5c\x19\x05\xcb\x0b\xa2\xe7\x89\x1e\x5c\xa7\
-\x98\xc6\x1d*\xc7\xdb\xe5&\xf2^\x97\x1f\x991t\xcc\
-\xc8<}7\xa3\xd6\x813fG\x9f\x91\xbdT\x81\x91\
-\x9eqJ\xc3ma\xca\x88\x19C\xe6\xb5g\xfa\xdfj\
-\x14\xfeO\x0ff\x8f]\xb3\xfb\xdd\xec\x16gJ\x8f\x96\
-\xe1\x85'\xf4\x83F\xd9b\xff\x96\x8e\x10\xbe\x8b\xdeH\
-\x9cb\xa23\xf2\x86g8\x88\xf0/\x121B\xdc\x06\
-\x09\x06\x02\x19ME0}\x0e\xa5\x0bC\xff>\x87\x1a\
-\x8c\x82\xf3\x83\x17\xed\x07b\x87Ae\xae2\x15\xbb\x17\
-\xbfc*v\xd7\xdc5\xe7z\xc5\xd8\x95\xe1F4r\
-(\xf8\x06\x845\xa2\x02\x8c\xe8\x98\x13\x9f\xf2Xr\xad\
-\xb1\x10\x046\xc41\xc4/]\xfd\xf5\xf7\xf7\x1a\xb6h\
-1\x1c\xeb\x81\xd7\x88\xa1\xcd|\x89\xef\x03\xafq|\x04\
-\xc9\xde\x1d\x81\x92\x97Y)\x84\x7f\xdc#\xd09\x13r\
-\x12\x06a\x16>h\x80n\x8d\x80\x9e\x10w\x12\x19\x06\
-}\x11=LDh\xf4\x07g\xf0@1S\x87b\xa6\
-\x9dY\xc0\x04\xb4\xc3\xba\x93\x98\xf4\xa4Dz\xba\xae\xe4\
-\xe9^\x8c\xf1\x8c\x8c\x8e\xfbc\x14A\x80Sa\xb7\xbf\
-\xc8\xfd\x95 \x9b<\xa31Eh\x81k C+v\
-\xf7\xc5\xb0\x84\x00\xc1Y\xfa\xc8\xfdY+\xcc\x89\xf88\
-\xec\xcf%aK|\xbbH\xf3w\xdc\x9fC\xd0O\xc2\
-S^q\xba\x94\x03\xcf\x04\xee\xea\x8cM\xc6Y\xa0\x0a\
-M\x95\xf7\x8f\x89\xab\x84L\x83\xa5d\x84\x7f\x09\xda\xf7\
-py\x9e\xf3d2\x9b\xb5\xe2\x91w\xe4\x0e\xa2\xe1\x04\
-!@c\xcf2\x93Q\xf0\xf9\x9c%$\xde7I\xa6\
-\xf4r\xf0\x11\x94\xe3\x151\x8c`r\xc0\xd0\xf9\x02\xa1\
-&WpYh\x06:b\xffF\xfc\xb3\xc1\xe6aA\
-G\x0aR\x00Dg\xd2\x0b\x1c-;\x97*\xb6!5\
-)\x9c!\x08\x0c@%\xc3\x87i>\xcc\x05?07\
-\xe0\x17\x9c\x95\xf0\x04\xc8`\xa1Z\xf4\xc3$\x9ah\x06\
-izD?r\x87~\xf0E\x8b\xe8\x87\xf5\x0c\x03|\
-O\x96\x0c\xc9\x84e\xc9\x5c4\x16\xb3\x04x\x08iD\
-(K\xc6-\xc7\x86\x0d\xdc 8\x22N>\x80\x22\xc6\
-\xaf\x7f\xd2\x85u\xa2\x1f\xc4\x88\x83\xc5\xca\x10\xb5X\x8c\
-\xd4<\x1e\x1fM\xa1\xb2\x95\xce\xd4l\xe1\xae.\xb6\xba\
-wW\xdc\xfb\xf3\x99|\xda\xd1\xc9;tx~0X\
-\xb1c\x9e\xe3=\xc6m\xb6\x8c;\x09\xe3\xe6\x91\xa1k\
-\xc0\xfc\x08h\x1a\x86\xc4\x16\xf5\x03\xe7\x0a\x16\xf1\xa0\x05\
-'\x8b\xf4@\xc3\x17\xe0\xc8\xa3O2\x91\x05\xd2JK\
-\xbcT\xd4>[;\x1b\xd9J\xa2\x13\x90\x01?\xc4`\
-\xeay\x06\xc4\xd2A\x10\xae-\x14\x02\xb2\xd5VG\xef\
-\xd1m't{w\xfd8\xdb\xb6ml[\x1f\xb1m\
-\xdf\xca\xb6\x93\x90mq)\xb0\x02\x92mK\xb2\x8d)\
-\xee\xc8\xb69 \xdbF\xc8v\xea&\xdb\xae\x90mw\
-L\xb6\xedC\xb2]\x88h\xa0\x15\x16\xae\x8d\x14\xaa\x92\
-\xa4I\x88\xb3P\x02\x9dDKBv\x845\x96\xf4\xcc\
-\x8aqm\xf33{\x94\x9f\xb9\xa3\xfcL\x17;-\xe9\
-Y>L\xcf\xf2Qz\xe6w\xe9Y7\x9b1\xaa\xdd\
-,\x83R;\xc5CL(\xc9c\x14\x8e\xdf\x92=\x1e\
-\xaf\xce\x1c\xae\xae-y\xf4\xf7\x92G\x98\x94\x11,\x18\
-\xd3<\xb3<\xce\x07\x97\xb7\xa3\xbb_\xc5\xc7aP\x9d\
-\x09s\x86\x86\xf8V\xfb\xd4\xad\xc8\xe8\x1dK\x87If\
-/\xf6\x00\x05\x05\x99I\xf2\x92\xdf\xd0\x86\xa0u\x88\xc2\
-\xe4\x92\x89\xd1U3\xec\xea\x82\xa1\xa5\x85\xc9\xef.\xb0\
-O\xac\x03^\xd4I#\xe7\xd4\x92\xd3\xc2\x1fIJK\
-t\xa1\x86x\xa4%\x95df\x9c\xe9wH\xa4\x8bu\
-\xdc\x19\x87\xde\xba\xb2\x80\xb5\xc0\xb6aQx\x0e\xd5\xc5\
-J}\x9f6\x9aJ\x8d\x00\x9a\xc4J\x81~,\xb0\x1b\
-\xdd\xa1!f\x17\xc8\xa8\xb7\x9a\xf6n\x8d\x13\xb7\x10\xc5\
-\xb38\xfaQ\xfe\xa0\xb0/o\xae\x98\xf1\x1a#\xfe\xdd\
-f#\x09\x8a5\x0c\xf3\xd6\x97\xd4\x11\x81\x86G\x8bt\
-\x16\xff\x92\x1e\xc2\xf9\x821\xd7\xd9\xd0a\x04\xc5s.\
-\xe0\x0cv\xe8\xf0\xc7|\xe5t\x09:\xdcr\x12\x06\xe0\
-9Lr\x09#\x19\xc9B\x14\x93\xa9\xdan6\xc9C\
-\xa4\xaf\x96\x22\x83\x22?+\x13\x0e\xca\x8c\x03N\x99\x1e\
-\xb3\x1c\xd3\x85K\xda\xd1@@\x00?\x98%\xbd0\xa2\
-+L\xe5J\x86\xa4%\xef\x22\x8da:G\xf7\xa8x\
-\xe9%\xb3\x8b\xc2\x88\xb0\x0fF\x8f\xac\xa9\xb9\x81{c\
-F\xa1\x99\xa9\x90\xd616{\xe6- \xfa\xec\x17\x94\
-(\x1e\x9c\xbd\xc4\x1cdw\xc0=\x1b)\x12\xc0\xbf\xa0\
-c\x8c\xc2\x908S\x02\xdc\xd8\xb2\xc7\x13\x83FrV\
-\x042\xc2\xd1\xc0\x8b\xddFU\x0c\x5cf\xabM>n\
-\xb4YLz\x90\xb0\x18\xdb\x05S\xde\xaa\x0f\x0c`\x1b\
-:\x02\x9d3+<\xd0\x1emS\x091\xa4\x04^\x5c\
-;\x0e\x93\xe6\x16$[\x04mB\xf2D{S\xe4{\
-8X6r\xa1R5\xa9\x0b6o\xa9\xfe\xcc\xa4i\
-\x898M\xe8\xbf\x96\xc8\x94@\xadZ\xb5I\x17m\x8a\
-$\x97\x89>\xc4\xe7c\x1d|%\x18\x97\xd0\x19\xc5\xb6\
-\x99R\x8am\xe7b\xdbZK,5\xce\x1co\xed>\
-G\xec\x8e\xfa\xc1\xaa\xbd\x0aYn%q\x01\xe2\xaf2\
-Uhg1$\x1e\xf87Gz<\xe7\xb71\x96a\
-\x95\xac\x1a\x9e\x80!\xdb\x98\x5c\xd4\x8e\x8b%\xa7\x82\xd3\
-\xa0\xcd\x97@h\xa8\x9f\x06X{:\x08Q\xd6H\x92\
-c\xb9IM\xd51)\x14\x85\xd5\xb1\x14\x8e8\x83)\
-\xf1I\xea\x02\xb6D\x1dV\x94\xc4\xd0@\xc0%\xdd\xf7\
-\xf6\xbe\xf5<\xba\xe5\xb8\xf3&q\xc7?\x22]\x01Z\
-\xe2L\xa2\xff\xa3\xc8/\x09%\xd3S0J\xab\xda\xf3\
-r\xfbP\xcd]\x87\x9a\xbb]\xfa\xe3\x88L\x9f?\xb8\
-U\xe2\xbd9E\x99a\xeb\x8e\xa4<\x93K\xe8\x8e\xe4\
-0\x11\xfa5(K\xdb\xd1r-\xf5>J\x09f\xcb\
-\xad\xb6Q\xde\x89u\x22K\x90m\xc4\x12c\x8d\x94\x1a\
-t\xa1OY<\x1e\x94(J\xadH~z\xa9AZ\
-\x19o\x09\x18l-}\xdd\xb4\x8c\xc8\xb2\xa0\x09\xad\xc2\
-\xee\xdb\x91\x8d\xf23^\xf3\x10\xe0\x8eBO\xf0\xe6\x0e\
-`\xfe\x84\x94\x9a\xab\xc4\xa9s\xaa2\xd3vWq\xbb\
-+b\xe2\x85\x85\x96\x1c\x5c\x09O,+\x1cl76\
-\xd8\xee\x8c\xd5\xbdR\xd2\x84\xd1\x0ddH\xbf\x8c\x97\xb8\
-\x11\xa5\x1c\xad\xc4\x07\xd1\xe3\x84,X\xc2\xb3\xc4\xe0\x91\
-\xfd\xe3\x9ea\x5c\x93$\xba\x92\xdf\xfbjj\xb0\xda\x9d\
-\xdfW\xca\xf0\x095\xbd\xe5\x80?'y\xb2\x12\x1f\x14\
-\x97V\x8b\xdbw\xabe\x81\xb9\x5c\x07o{\xd5\xe5j\
-\xbe\xe4\xcf\x0a\x1cy\x89!{\x80\xbb\x00EL\xf0\x0e\
-V8\x1e\xdf6DV\xb6\x09\x8b\xa6#\xa1\x1a::\
-o[\xdd\xf3\x07\x08\xbb\xd3q~dJVDwS\
-\x92\x88\x82xB\xaeC\x98\xb6\xf0l6\xf4\x1d\xcf\xc0\
-\xc9\xfb\x0e\xe4\x1c\xaeT\xac\xbbcvG\x91\x22\x04}\
-\xc7v5\x18\xbcEz`K\xf0\xd3z[\xe1\xad\x06\
-\xfcU2\xcf\xa1\x10af`\xd8q\xa9\x93\x07z\xc5\
-\xe8\x8bGQN\x18X\x96\xc3@\xdcU\xb4QO\x13\
-9nt\xbb\xc7\x13\xbe\x06{\xf1?\xc6\xc8&\x02\xb0\
-5\x00\x00\
-\x00\x001:\
-\x1f\
-\x8b\x08\x00\x00\x00\x00\x00\x00\x00\xec}ms\x1bG\x92\
-\xe6\xe7\x9d_\xc1\xd3|\x19E\x00\xc5|\x7f\xd1\x8cw\
-c\xd6\xde\x99\xd8\x08O\xec\xc6\xcd\xee\xde\xde'\x07\x04\
-\x822ohRGR~\x99_\x7f\xdd\x00\xc9j\x08\
-j4\x80\x06d\xc9g\x94)\x03\xd5\xd9Y\x95Oe\
-eeeWU\xff\xe1\x9f~\xfc\xee\xfa\xec\xfb\xc5\xdd\
-\xfd\xd5\xed\xcd\x17/\xb0\xc0\x8b\xb3\xc5\xcd\xfc\xf6\xe2\xea\
-\xe6\xcd\x17/\xfe\xf3?\xfe4\x8d\x17g\xf7\x0f\xb3\x9b\
-\x8b\xd9\xf5\xed\xcd\xe2\x8b\x177\xb7/\xfe\xe9\x1f\x7f\xf3\
-\x87\xff1\x9d\x9e\xfdyq\xb3\xb8\x9b=\xdc\xde\xbd:\
-\xfb\xe3\xc5\xed\xeb\xc5\xd9\xbf^_\xbf\xbb\x7fXf\x9d\
-!\x15(09\xfb\xeb\x7f\xfd\xf9\xec_~|{{\
-\xf7p\xf6\xef\xd7\xef\xdeL\xff\xf5\xe6\xac,3\xffk\
-U\xe6\xab3+\x00g\xff\xfc\xee\xea\xfa\xe2LQ$\
-^\x9e\x9dM\xa7M\x11\xf7\xdf\xbf\xf9\xcd\xd9\xd9YS\
-\xbf\x9b\xfbW7\xf7_\xbc\xf8\xf6\xe1\xe1\xed\xab\xf3\xf3\
-\x9b\xfb2k\x8b+\xf3\xdb\xef\xce\xff:\xfb~\xf1\xa7\
-\xdb\xbb\xff\xb5x}\x8e\x05\xce_\xd4;f\x1f\xbea\
-Y\xd3\xa6\xfc\xff\xbaZ\xfc\xb0\xb8\xfb\x97\x1f\x1f\x167\
-m5\xee\xcfy\xfd\xf6\xab-\xb7w\xe4<GX\xbf\
-\xefb\xfe|\xe3\xdbww\xd7\xe5\xf6\xee\xcd\xf9\xc5\xfc\
-|q\xbd\xf8nq\xf3p\xdf\xd4\x12\xbb\xe4\xf3J>\
-\xbf[\xcc\x1e\xae\xbe_4\xc5|\xd7T\xa8\xbd\xb3)\
-\xfb\xb7\x1d\xe2\xbb\x8b\xcbg\xea\x1f~\xf8\xa1\xfc\xc0K\
-\x22\xcc\xccs\xa0s\xa2iC1\xbd\xff\xe9\xe6a\xf6\
-\xe3t\xfd\xd6\x06\xcc\x0f\xddJ\x00p\xde\x5c\xab\x94\xbb\
-Q\xbd\xfa\xf1\xfa\xea\xe6o\xbd\x95Y^\xed\x96\xde\xe8\
-\xd3\xdb\xe6\xef\xf9\x86\xa7\x8cr\x7f\xfb\xeen\xbe\xb8l\
-\xee\x5c\x94\x9b\xc5\xc3\xf9W\xff\xf1\xd5\xf3\xc5)\x94\x8b\
-\x87\x8b\x0e\x9b\x86\xe9\xfd|\xf6v\xb1V\xeeS\xe6\x0a\
-\xaf\xd9w\x8b\xfb\xb7\xb3\xf9\xe2\xfe\xfc)\xbf\xbd\xbf\xab\
-\xe0\xb8\xcc\xb8\xba\xf8\xe2\xc5\xd7\xb3\x9f\x16w\xdf\xac~\
-\xffpu\xf1\xf0ms\x99b\xf9\xf3\xdb\xc5\xd5\x9bo\
-\x1f\xea\xef\xef\x1bm\xf9\xe7\xdb\x1f\xbfx\x01gp\x86\
-\x14gO\x17n\x1b\xce\x97\xd7\xb7?|\xf1\xe2\xfb\xab\
-\xfb\xab\xd7\xd7\xab\xe2\x167\xb3\xe6\xeb\xf4\xf5l\xfe\xb7\
-7w\xb7\xefn\x9a\xc2n\x16?\x9cun~\x92\xea\
-U[\xdbF\xa0\xb7w\x8b\xfb\xc5\xdd\xf7\xab\xdb\x9f\x00\
-x\xf5\x5cm(L\xcbKORu/\x89\xad\xdfu\
-q;oph\x98\xbe\xb9\x9d6|\xbf\xbf\xba}w\
-?};k\x10n\xda\xf0\xef\x1b\xb4\xafg\xf7\x0d\xed\
-\xf9\x7f\xde7\x1c\xcf/f\xdf_]\x9c\x7f\xb5\xb8\xff\
-\xdb\xc3\xed\xdb\xf3\xfb\xa6\xdf\xbf\xbe\xfdq\xbd\xe4\xdbw\
-\x0fo\xdf=|\xb3h\xbb\xce\xaa\x0a\x0d\xf2\xb5\x19V\
-\x97\x97E=g\xae3X,\xcd\xc1\xf4\xf2\xeaz\xb1\
-\xaa\xe7\xf9\xb7\xb7\xdf-\xce\xdf^\xdd4\xb0\xdf\xdd6\
-_\xe6\xf7\xe7\xb7?\xfe\xf4fqs\xde\xdcq\xddb\
-y>\x9b?,\xbb\xe9\x86Loo\xde|\x90\xfd\x8f\
-\x17o\x1b}3/\xfa\xc1\xcb?\xd5\xcb\xff\xf8\x87\x8b\
-\xc5\xe5\xfd\x93V\xb4\xdf\x11\xda\xdcF\x89\x17\xb3\xbb?\
-\xdf\xcd.\xae\x9a\xae\xbb\xc6d~{}\xbd\x987\x0a\
-2\xbb\xfea\xf6\xd3\xfd\xb3J\xad\xdf\xc2\x8e\xde\xf0\xb9\
-o\xa0l\x08Z\xd8\x1f~\xban\xe4m3\xa6\x0d\x8b\
-\xc6r\xfe\xf6r\xf9\xf9\xfd2\xeb\xb6\xd1\x85\xab\x87\x9f\
-^\xe1\xef_\xac\xe8o//\xef\x17M)\xd0\xfe~\
-,\xa2\xa5d\xc7|qv~\x00kx\x9f5n\xb0\
-&\x5c\xb2>_\x97\xe5\x1f\xff\xf0,\xfc\xdbFQ\xde\
-.\xe6\xad\xadZ\xd3\xa5\x87\x9f\xda\xae\xb9N\xc6\x17\xef\
-\xa9\xed\xdbo\x9a^4U8k\x0c?\xb4\xff\xe2\x06\
-\xc1O\x8d\xc0\xed\x05X^\x87\x8d\xeb\x7f\xff\xe2\x85\xc3\
-&\x83\xf5b\xa7\xb7wWo\xae\x1a\xe5\xe4%\x91T\
-\xd2\xa5\xa8\x1d\x19\x08\xa9\x11\xf8\x97+\x1f\x9a.\xe5\xfb\
-nv\xf7\xb7\xc5\xdd\x1a\xaf\xa6\xc1\xe7\x7fk\xc9\xffx\
-ww\xfb\x03\xfeeq\xb3\x12\xa7\xe1\xbd\xb8i\xb5\xfb\
-\xdd\xc3\xed2\xe3nq\xf9\xbf[S\x03O\xbf\xfe\xbb\
-\xfez\xff\xfe\xaa\x8cO\xc6\xf1\xd5\xa3m\xfc}\xd3\x19\
-\xde\xce\x1e\xbe\xad\x1a\xd7\xfebV\x7fT\xc2&\xe7/\
-g\xad\xdb\xd0\xfc\x9d}}\xa6\xcd\xb7\xa9.\xbfN\x91\
-\x8av\xb2\x97\xb9\xcf\xa4\x7f?{\xb1\xd6\x0b\x1a\xd3r\
-=\xbd{w\xdd\xf4\xf6\xef\x177\xb7\x17\x17M'\xb8\
-\xbb\xfd\xdb\xe2\xd5oa\xf9y\xfc9]Z\xfdWX\
-\xe0\xed\xc3\xefW\xf0L\x1b_\xe7\xee\xe1\xd5M\xe3\xeb\
-<\xf5\x94f\xa0\xbf\xb9oF\xa8\xef\xbex\xd1Z\xa3\
-\xc5\xef\xa0\xc8\xcb\xb3\xbb\xdb\x87\xd9\xc3\xe2w\x18\xf0r\
-Eq\xbd\xfc\x09\x13x\xb9\xea>+~;\xc1\xbe,\
-s\x04\xf0\x95C/\xf4}\xc8\xcb\xa7\x88\xfc\x16\xe0G\
-\x83\xfd\xd7\xd1`\xff\xf5P\xb0\x0d>/\xb0\xa9\x0b\xb6\
-\x1d\x80\xf5\xd7#\xed\xc9\xd7\x87\xda\x13\xfc\xcc\xecI\xf4\
-\xda\x13*z\x08\xf0\xa3\x95\xfc\xebC\x95\x5c\xe2\xf3R\
-\xf2\x18\x80\xfb\xa8~\xc1rP\x97C\x9d\x02\xa4\xa8\xf7\
-o\xf5\x09\x964B\xc5\xda\x8f\xf7:\x06\xe1\xad\xa4\xe3\
-\xfd\x5cC\xe4\x01?\xb76\xd6>~\xae!\xb6\xbe\xcb\
-\xfe\xac\x87\xfd\x5cC\xf4\x0f\xfb\xb9\xed\x97\xd9\xf5\xcep\
-,\xe7\xba\xaf\xbe\xbd[4s\xf3\xdf\xae\xf3b\x02\x7f\
-\x86l\x9d-Q\xe8\xf2\xd2\x9b\xc7\x8c\xff\xbc\xb9zh\
-&\xdf\xef\x9aY\xd8_\xdb)\xe1\xbf\xdd4\x13\xb25\
-\x8a\xff\xa8\xca\xfb\xdd\xec\xe1\xee\xea\xc7\xdfa\x01!R\
-\x99@\x9b\x0a\x92\x01\xca\x84\xb0h\xb6_\xa6A\xc5\xd0\
-\x13_.\xf9\xcc\x1b\x05\x94,\x80\x98\x8e\xab\x9cF\xe9\
-\xa8\x98\x0b\x83\xc72\xe7r\x83\xe6r\x83\xe6\xae\x9dE\
-;\x89ar\x8f\xfel\xaa\x08\x11\xd3\x90\x8a\xbc\x96\x14\
-\x9a\xed\xa9\x22D,\xc3*\xb2\x90\x8b\xf9<\xdfc=\
-\xa0!Dl\x1f\xd6\x90\xfa{\x84\x86TX\xfa\x10\x0b\
-\x1e\xad!P<\x125\x97\x1a\xf2\xfc\x83\xb0\x80\x85\xca\
-\x84\x8b\xa4\x13\xc5bJ/WU\xc5e\xfb\xa32\xac\
-\x0a\xff\xa9\xc9p-\x8c\xc0\x18+\x12\xfa\xe2EP\x91\
-\x10\x13Z\xd1P\x87fg\x8d`b}\xb1\x15\xbb\xda\
-\xa6\xb5Q\x98\xd874b\xdf\xc9\xf5\xd9y\x0f\xeb\xdc\
-P\x89='\xd7\xa7\xd3\x97\x0aZ\xbf\xbe\xe0\x11,\x0a\
-\x22\x09\xe7\x04\x0b\xb0\x0a\xdbd\xda~C3\x9e\xb4\xd7\
-L\xc1'\xa6E\x1d\x94'S\xc9\x82B(\x1d\xe5\x89\
-\x22\x19j\xf4\xac=P\x02QA\xd2\x9e\xd5G\xa28\
-\x93\x13?\xab\x0f\x156u\xb2\xdcC}\xe0D\xb1\x15\
-\x82\x1db+\xa4m\xda7\xb6B\x88?\xeb\x98\xe3y\
-\x04\x0dQL\xafcNH\x0aMPJ@B\xa3\x12\
-\xee\x05Q\xfdS\x18s\x98HO\xa4\x22\xe4\xc3*2\
-[\xb4i\x805n\xb2\xce\xd3\xab\x08i\xbf\x8a\xf8)\
-\x8c\x08\x14\x8e0\x8f\x09\x14!0\x91\x89AA\x08\x9a\
-L\x85\x0b1\xa8\xad+\x0cd\x84<+L\x14Ie\
-$\xaa\x0a\xe3\x85\x1d\x22\xe2Ya\xac\xb8\x03Z\xfa\x93\
-\xc2pIW\x10\xd0\xdd\x15\x86I?<\xea0\x9dl\
-\xd4a:\xe1\xa83,1\xf0!]\x04\x8a\x05\x8b2\
-\xd9`_a\xd0S\x85\xaa\x19N\xef\xc23p\x7f_\
-9\x86\x83\x96\x99\xd9:\xeb^B\xd0\xc4\x16S\x9eh\
-13\xf0\x5cLeB\x05$\x18\x9a|j\x07c\x12\
-\x22\x96\x96\xc8\x8b\x06\xb2z\xed8,\xc5\xdc\x8d\xf3\xb9\
-\xe3x\x11#r[\xe5\x5cn\xd0\x5cn\xd0\xdc-\xfb\
-\x96\xa6b\xb6\xe0\x1e\xd5y\x01\x8d^\xe7\xc5\xf1\x08X\
-\x86\x9a\x02\xb5\x8en\xa6\xae\xec\x8e\xa7\x19\xb5Y\xa1i\
-,\x13D-\x8a\xc4\x93)aI\x16\xcf\xea\xbc \x16\
-&\xe3\x90g\xe7E\xbdppf>\xfb.(E\x92\
-\x95qEC\x95f\x0f\xdf\x054>lg\xc0`w\
-;\x933\xb7\xcd)\xf5\x0e\x93!\x9e]\x0cO\xc5\x0b\
-$%\x01\xdb\x8b\x8d:Z\x9f-\x03\x87\x0d6\x0a\xc2\
-\xe8[m\xda\xfc\x82\xe6\xbb\x9aK\xb0\xd8(\x22Q\x1d\
-9\xf4\xc5\x80\xc8\xbb\x97A\xbb\x9b\xe4\xd7\x19=\xacO\
-j\x97@\xa3\xd7.\xd9x7O\x0b\x99\xb3`ku\
-\xb4\x80\xa2\xa1<Z P\xf7\x94\xd5t\x80\xda\x1e\x15\
-\x5c\xdc\x15'S\xb3\xa2\x1c\x1a\x1d\x8bd\xc5\x5cM\xe2\
-\xd9\x22\xa5\x16\xb7$\xaf\x16\x09\xad\x10:\xab\xd4\xa1\x5c\
-\x0a\x0aR`\xc7\x22\xb9\x98\xc0\x1e\xf1\x06\xcd\x0fw1\
-2\xd8\xa3\x8bU7\x7f\xb8\x8b\x0d\xdf\xb7\xd97^l\
-T\xae\xafo\x91\xc1\xa6\xe2\xbb\x8e\xabz\x97\xfb\x1e*\
-\x0f\x00\xf0\xf1U\x9e4\xb7\xa8\xfc1\xdcV\x95P\x9e\
-`\x11KDY\x8e\x10\xa0h1!*\x16\x9a1A\
-r*)\xed\xb4\x98\x12\x8b\x00U]G-N\x0e\xee\
-\xcf\xba\xaeQ \xd2\xc2\xab\xae\xaf\xd1\x5cn\xd0\xdc-\
-'>(\x9c\xa6{\xc4\xd6\xd4O\x14[\xd3<Ul\
-\xcd\xf0\xe4\xfaB\xea\xfd\xfar\x8c\xe8+1\x1a\xda\x04\
-\x0b%\x0aa\xa3\x14X\x1c\x96YX\xd0\xa35\x8e\x82\
-X\xc2\xa5\xb9\xc6\xa4E\xb5\xa30B\xc5\xd0\x95\xeb\xc4\
-\x18\x09\x8a\x09>\xcdj6h.\xbb4u\x9e#`\
-\x11\xfbD\xde\x12\x07\xf4\xe5\xf5u\xb3ti\xa3I\x07\
-&\xc5\xcb\xa9\xf9\xde|apF\x9c\xba\xeb\x04\xa7u\
-\xc7\xe0\xd9U\xa3\xe8\xfaq\xfa\xec\xe6Q\xec\xd8\xf6\x9b\
-\xd0a\x985\xe3\xcao\xfe\xe1\x1f\xfe\xe1Q\xceZQ\
-\x0ck\xdd\x9f\xde\x09\xce|\xfe\xc18C\x05\xb4\x91\xb2\
-\x9f3\xf5p\xfe\xe1\xdb\xab\x87\xc5\xe6\xc4\xa9\x0b\xe8#\
-\xdfO\x03@M\xf3>\x005-\x07\xc4<\x08>M\
-\xc7\x13\xc3W\xfb\xda\x7f\xff\xe5\xeb\x7f\xfd\xea\x1b\xfaf\
-7\x88*\x9e\x15\xe7.\xfe\xb5Q\xaal\xdd\xda\xf5*\
-\xdc\x9f\xfe\xf4/\x7f\x04x\xb1\x86\x03m@T\x85\xec\
-e\xf3\xe5\x97\xef\xb3\x91\x8a\xc8\x11\x03\xdd+\xdc\xf2\x9b\
-\xbe\x19\x22'\xee\x01iJa\xe1\x8a+\x02\x15\x16\xf1\
-\xfa\x08\xc3\x8a\xb2\xda3\xc0)E\xf9qf3^\x0c\
-\x84-r\xf0>rhG\x04^\xab}\x98\xae\xd5>\
-L\x8fU{\xdcR{=B\xed\xbdPr\xad\xbc\xb6\
-?\x8fUw\xdaRw\xdf\xa7\xee\xd6\xa9\xbb\xd4\xbaG\
-\x01\x00\xaa\x95\xb7\xd5\xef\xb3\xf3\xea\xbb\x1c^{\xff\xa6\
-\xcfc\x91]<\x96\xea\x8b\x02U\xa7\x02\xa90\xc8\xb3\
-\x83\x89\x9cE5\xf4HU\x8e-U\xf6#W9W\
-\x96\xe7\x0f\x8d\x1f\xd6,\xd0\xbe\xf9\xb7\xd7\xff\xa7\xa9\xe0\
-\x92f\xf1\x7f\xdf]\xdd-.\xeaz\xfe\xfd\x97\xef?\
-\x19\xd3\xa6|X[\x13\xbe\xbe\x22\xfcY\xda\xb5J\xac\
-\xc6\xb4?\x5c\xbdz\xfb\xe6\xf2\x7f..7\xb0ZV\
-\xe1\x9b\xabZ\xf27\x0d\xe1\xea\x9e\xf3\xa7\x9bZ\xc9\xce\
-\xd7\xb869\xbf9\xa5QEb\x87\xcf\xdb\xaaVA\
-\xe8s4\xab\xb5\xfa\xf2\xf9\xd9\xd5Zy;\xada\x1d\
-_\xfd\xad\xb5\x8f#\xfbjG\x8e\x9b\x13C\xf6\xd5^\
-0\xf6\xe9\xc0S\x97\x12\x04\x0e\xdei\x03(A\xecu\
-u\xc7\x14\xa3 a\xd6\x8eP\x89>\xaeh\xf9y\x8a\
-Vg:\xfd\xb3 \xe7}\x94\xce\x8bc\xba\xd4\x9e\xe3\
-%\x02k\xf7\xd9 ir\x18\x8a\x08bh_\x1c\xa3\
-\xb3\xee\xb1\x88\x83\xa0-\xa6\xed\xf2\xc7\x8f\xd8\xc6\x9a\xee\
-\x1f\xb5\x8d\xab\xcfs\xfc6^\xe7\x8b\x9aa\xcf\xde\x0d\
-YI\x95\xae\x8f\xc3X@\x14TW\x91\x9d\x0d\x9a\xcb\
-\x0d\x9a\xc6\x11\x8a\x92\x16\xa8<\xbc\xecK%=&S\
-*\xa9\xc9\xb1\x8c\xe3\xc3\x04J\x82\xba\xd3\xc4\x8b)\x90\
-\xeb\x04\xa1\x84\x07\xf8\xcb\xe1V8\xb9%\x0eS?\x81\
-%n}\xac\xf5\x92z\xcb\x97\xfc\x04g\xeda\x8a#\
-'\xee\x95\x13\xf7\xcc\xdd\xdb\xbc\xc6\xfd<\xa2\xf9\x0b\xb3\
-\xe8oi\x97}\xba=gQF\xaa^O\xed\xee\xf5\
-b} \x8b\x05\xc3\xf5\xf4\xfa\xea\x22'\xd0\xd7\x8f\xd6\
-\x08.v\xeaF8\xf9$\xd34\xe4\x18\x8b0<\x9c\
-\xf8iQ[\x066\x89&Y\xd8\xd0\x81p\x82\x05\x8d\
-\x9b$/\xf7\x9b\xaf\x9e:,`\x1a~\xb8\xf8\xd5\x01\
-\x88f\xd8\x1f\x19<\x18\xaf\xa8=2\xa6\x12\xd4\x07\xca\
-P\x00\x019\xeb8*\xc5\x15P\xea\x03\xe5u\x9a\xcb\
-\x0d\x9aF\x0c\x82\xe2\x81\xa4\x03\x8a\x81K\x85\xc0B\x89\
-\x86\xd0\xaa\xc7\x14\xb5\xa8'\xc7\x0e\x03\xe6\xc9cB\xf4\
-y6\xfep\xe4\x88\x22?\xedN}\xdaP7\xfdB\
-B\xdd\xf4Y\x87\xba\xe93\x0eu\xd3\x09C\xdd\xa7\xef\
-\xfdN'\xeb\xfdS\x8cb\x1c\x946\x99Z\x09\xb66\
-}b\x83:\x91\xeb1\xec\xfa\x14\xb3d\xfb\x89\xc94\
-\x0e\xb0\xf0\xcd\xce\xcb\x87\xc5\xddN\xfb\x01W\xa4L\x82\
-/\x9a\xfb\x16\x7f\x9e\xbd\xbb\xbf\xbf\x9a\xdd\xfc\xf3\xf5\xbb\
-\xf6\xfe!\x0e\xed\x04\xe6\xe2\xab\xe6P\x8f\xd9\xc3\xd3)\
-D\x9c\xa2(\x9d\xa5\x00\xebL\x99\x84W\x8b\x02V%\
-\x9f\xb4=\x04\xc4\x8e\xd1\x1eT\x04\xda\x0f.\xa6\xb6\xef\
-\x90{\xe4\x89\x81!r\xfff\xce\xacs\x16.\x12n\
-T7\x83\x89\x17\xc8\x08\xaf\xc3\x0b\x16SO\xcb:\xc0\
-P\x16$V\xb2\x83\x82\x0a\x838\x1a\x97\x94\xb4hA\
-|.s\x8aE@\xdc$\xaa\xfd*\xc4a\x18\xf9\x5c\
-\xf9)r\x09\x93Lz\x92\xb0\x12\x1d\xbc\xa4\x81)c\
-x\xcbE\xc4\xf6\xcdn\x9b8\xd4\xe0\x95\x97T\xc9:\
-\xa88\x171\xb2\xe8\x0c9^\xda\xdf\x18U.(,\
-\xe68\xbcf\x9a\x99\xf9\xd9NF\xfbI\x9c`\x16\x17\
-\x0f\xe3\x09c\x81\x0ce\x7fy0BD\xe0\x83\x081\
-\xf1\xe1\x08e\x09I\xb0:\xa7\x86\xce,\xb5\xa0)@\
-G\xa9a\x08\x94\x09\x17~\x84\x85\x8aj\xf2r\xa7\x0a\
-\x14\xe3\x0c\xf2\xc9\x14-\x0b1:N,\x8b)X\xe6\
-(td\x18\x1d\x94C\xd1!-\x1e\x86V\xf5\x87\xb3\
-\x90\x05V\xfd\xa94\x9d\xb5\xf0\x5cP\x9dx\x87\xe81\
-\x96\x5c~&p8\x0a\xac\xca\xc3\x91\xe4\xccOEG\
-\xaa\x86@\xd5\x8c\xaa-K\xc5\xe0@\xb4Gm\xc1D\
-\x1e\x85\x0e\x8e\xd0\x91O\xcb\xc6$\x1a\x89\xc6D\xa0X\
-\xfb\xd1\x09S\x01\x0b\x03\x1a\x83\x90\xe4)m\x0cS\x85\
-F\x0a30Q\x1dW\xb4\x04\x03!Y\xed?@\x85\
-\x11I\x86W\x1d;\x0a\x85-\xc1\xa1\xe5\x06JQe\
-\xf5\xc9\x94d\x14\x1e\xbe\x83\xc6\xc0\xc1V\xc5W\x9d\xbe\
-\xc2\x82$\x05\x12\xb5\xc2\x92\xb9\xa4\x09\xad\xdb\x83\xa98\
-\x85\xb1\x1c4,M\xa6\xba\xeau\xab^\xb5\xca\xb5Q\
-\x18\xe9\xa0\xcd\x11\xc6C1R\xc9\x02\x86,\x1d\xcb\x9b\
-\xc5]\xac3\xe1B\x93b\x96Us*\xcdv\x8c\xb2\
-8\x9aj\xe4bJ\x8f0!\x03br;\xb5a0\
-\xb4\xf4F\x9f\xb0(\xb0\xf28\x9c\x06m\xb3\x0b\xd1\xbe\
-8\x1d{d9\xd2\x88\xd7\x8b\x02\xca\x98\x11\xea\x97o\
-\x83IYF\xd8\xe0_\xf8\x18N\xca\xe3\xc7\xf0}\xf6\
-2s\xdf^f\xdd}\x03\xd4\xdc\xe7\x17\x17\xbe\xeb\xc6\
-9\xd1\x18\xb7\x97\x19\xc7\x9f\xa0\xd1\x07\x07\xcai\xce\x02\
-`\xb4\x93\xedo\xc68\x1d\x1cp*8\xe8tp\xc8\
-\xe1;\xdd]\x88>\xd8\x1f\x5cHv\xef\x0f\xaf\xa5I\
-\xb6\xff\x86\xc0\x9c\xe7|\x86{o\x08T\x96\x18f>\
-\xbfh:)\xeey\xd2\x84\x0b\xd9\xe1p\x920~\x10\
-N\x12\xe6\xe3\x1c\x950^7\x8b\x90\xa6\x83\xe7\x8b\x8d\
-:\xea\x0ee`\x9b\x86\x9b,\x92<p\xb3\x08\xef\xdb\
-k)\x9c\x1b\x5c\x02\x08\xd3c\x8b\xb9\x5c\xcc\xda\xb4\xeb\
-~N\x11\xdc\xdd\x12_\x5c6i\x03\xcd\x11\xca\xc1\x99\
-C\xd6\x05\xd2b\xb1\xafu!\x01\xdc\xe5\xe0\x95y\xb3\
-\xdbj\xcf\xee@\x02<\xde\xd8\x0e\xfb\xc8}\x06y\xc4\
-\xb3\xdc\x913\x82\x1fq\x87y\x09ui6\xe67\xc7\
-^1(\x8c\xfd8\xe50N\xc7\x9c]n\xce]7\
-\xe7\xb7}s\xe0\xee<\xf9\xc8\x181#\xf4b\x944\
-\x8c\xd1\xd8\xb8\xc4f\x9cc3\x18\xb2\x191\xa9\xe1\x94\
-=\xb6\xabF\x1crP\x5c\xee\xb1\xcf\xdf0./h\
-\xe7\x83\xe2r\x8f\xcd\xf23\x99\xc3B\xd6Y\xefs\xc2\
-\xfa\xe6\xb9\x9d,\xa4{\x9c\x09>t\xbe\xf8\xf0Y\xa4\
-\xc3'\x9c\x0f\x1f\x87z\xf4E\xc5\x99}\xea\xef \xa3\
-\xd5\x7f\xfa\xb8\xf4g\x82$\x935u\x87\x02\x14\x9a5\
-\x90\x00\x051\xa0\x1b\xe4\x89\x02\xc6\x0a\xfe\xa4\xed\x95\xe6\
-\xc0\xe7\xaa\xeet\x8c\xe7\xaaP\x14\xc0\x98d\xcb\x83U\
-w\xe9>X\xed\x9b\x8a\x9b\x15\x22\x11\xad\x8f\x01\x95\x0a\
-a\x88\xd5\x85(\xc6\x059\x14\xb5\xa2\x97V\xc4Bl\
-\x87\x87\x0a\x1c\xc5\xc3%&SX\xb1\xf1\x1c\x11\xc1\x22\
-\xb1\xe1gt\x06\x87G&\xac\x08\xb9V\x93\x87h\xc5\
-1\xc4\xab]\x84\xc2\x86\x12\xa4V\xa37\xbe\xc2#~\
-\x06@\xe4\x94\x80\x84.\xf1\xd0|\x06Dr\xa9\x1fN\
-\xd5\xc5\xb0G9\xea\x10\xeb%\xad\xa5\xd9\x0d\x0e\x9d\xc0\
-(\x04h\x18\x01\xa0C\x110-\x22\x81HU%\x9c\
-K\x068V\x04\x88\x8bk\x98\xd4\x1e\xe2Y\xd0\x89d\
-\x8c\x5c0,W\xea\xcf$\xd7p\xcb\xa6\x8fkV\x8e\
-\xd1\xe2\xf7\xac\xf0\xb9k\xac\x9c\xd5s\xd3\xac\x1e\xeb\xf2\
-\xa8\xb1\xf3\x9a=\xaf\xd9;,0E\x0eN\xca\xe5\x02\
-S(\x8a\x1e\xea\xfb`\xb0^W&\xb6\xbeU.\xf2\
-\xcdx\xc9\x91\xb0\x88\x10r\x05 \xa2D\xa0\xf3'\x06\
-\xc3\xa0\x8d33;\xf8\xf1\x8e\x17\xf2$\xae=\x81\xc8\
-\x8au|\x03\xe5\xe2!\xa8\x5cc\xd2\xc4\x85\xc9\x91V\
->\xd3\xdb\xbb\xc5\xec\xe2/\x8b\x87oo\xdb\xda/.\
-\xdb\x92w\xc2\x0aJ\x18\x82\x18\xb7HEAC\x8e\xe6\
-k\x116\x0c\xf3Q\xfd\x87N:RZq\x05\xef\x0c\
-\x0c\x1e%9\x99+l(\x85\x5c#\xaa\xf9p)\xae\
-\xfd\x90\x8d\x90\x96\xf2\x84\xd6\xc2\xa0\x9eif\x85E\x14\
-\xeav/\xa7\x02\x04\x81\xb5\xcf\xac\xd3\xcc\xbb4C\xee\
-*\x14\x0d4\x0a\x9d`qs7oc\x00T,\xc3\
-HV\x01\x81\x12\xe1\x91\xd1d\xb3\x16\xf0@\x9bhI\
-\xc1\x84\x941\xbd\x8c|\x10B\x91\xfc\xb5\x97u\x84\xa3\
- \x1b\xe9|\x8c\xefeZ\x98M\xa5\xd3\xcb\xb8\xb8\x90\
-q\x9c\xa6\xa7\xf1.ve|O\x0b+A\x96V{\
-\xda\x14\xad\x98\x85D=\x86p\x9dh\xbeF4\xdc\xd7\
-H\xc3\x0c\xbc\xf5\xc4\x95\x90\xc4\xa5\xfd\x1a\xe4\xc2\x22\x13\
-(\x82`\x808a\xa0\xa2\x9c\xa2\x13\x85\x02\xaa\x9a:\
-\xaa\xa3\xe1I;ZA\xd5\xd4\xe88v \x85t\xcd\
-\xabK\xb0\xe0\xce|N\x8a\x0e\xc1\xf5~Wb\xd7#\
-\x8dT8h\xbbM\xe0H\xcfS)s\xf0\x89\xd9\x85\
-\xb7i\xdf\x98v&\x1d\xe9\x81\x07n\xb2\x1e\xf1\xc4\x8c\
-\x89}Pb]\xd8\xde\xcf\x08)\xf0dGB\x93\xe0\
-\xe1\x12\x9b\xb9\x0cI\x0c\x80&\xb1\xa7\xc4fn\xc3\x12\
-#\xcero\x89\xcd<\xc6HlV%^ck\xb1\
-\xc7I\xc4\x97s\xbc\xa4\xfd\x1f\xe3i\xf8\xe2\xf5\xfe\xc7\
-\xa42\x06W\xe6\xef\xc3\x01{\x1cd:\xa7\x99n4\
-\xe6\x188e\x97\xe3\xd4/.\xe6{+\x90\xec\xd4e\
-.._\xef\xaf@\xe2c$f\x1c\x92\xd8\x81\x1d`\
-o\x89\x99\x87%~}\xa9\x0b\x80\xbd%f\x1d!\xb1\
-\x88\x9dd\xe9\x84\x89\xc4\x89\xcc\xa2\x89\xc2\x18\x89a\xd8\
-,\x06.x\xb1\xb7\xc4`\xc3\x12s\xf8\xc5\xc6\x03\x94\
-a\x89!N}\xc0\xac\x99Y\xdf\x9ee;\xca#8\
-NSR\x9eX\xc9`\x14\xc5\xf6\xdc\xff\xa9\x17\x0f\x94\
-\xb4\xf6\x07\x16U\xc3\xe0\xd6\xa5\x22*A\x868\x99Z\
-\x16\xcb0\xb4\x97\x1f\xf0\xe4\xdf\xce.\xbaG\xd0:\x00\
-kg\x03\x12p\xd1\x80\x1a\xdaY'\xb9|\x9f\xe4n\
-I\xa1\x9a\x12#v\x94\xf6;o}*\x89P\x1f^\
-\xd7\x90rD\x9d%J\xdd\x1b\xb4K8\xf5\xe8\x95\x17\
-\xeb\xad\xbcR\x9d\xaab\x81\x94\xcc\xc7\xda\xaer\x100\
-S\xaa\x1f\xae\x05#\x91\xb9\xfbT\x19\xc0Cw\x91\xab\
-\x7f.7\xbc\x83\xd0\x8b\x86CZ\xb4Z\xc7\x01&J\
-/?&R&\xc7\xd8y\x07E\x90\x12\x98&P\x0c\
-\x93U\xe2e?.{\xb7\x0dYD\xac\xb5\x8d\x03d\
-~L\x9c\x1c>u\x9c\xb8\xa4!H\x85)\x0a\xb1\xa6\
-\xf8G\x85\x89~\x85\xa9\xe3\xb8\xf5\xfat\xf5\x91\x9cj\
-\xe1V\xd6\xba\x03\xb4>\xfa\xd9\xb8\xbaZ\xea\x91\xc8(\
-\xfe\xf1-\xae\x09\xf7J$uY\xb8Q\x01\x0eG\xea\
-F\xc2\x12\xd9\xf1Q\xac\x0d\x92VP(N\x91\xa1G\
-\xd1\x9f\x09\x1f\xdd\x88\x9aK\xaf\xf0\x01\x9d\x88M\x814\
-w\xad\xd1S(\x1a`\xe2U\x07\xb3$\x0bs]\xe8\
-\x15Q\x8cY\xe2\x93\x1aF)p\x9f\xf3\x8b\xac\xb3B\
-\x89\xac$\x9b\xad\xafP\x12q7\xaa2S\x01\xcaP\
-\xf9x\x1211\x1e\xc7>\x91\x11\x1b\xe5\x04\xeb\xca\x15\
-\xcdB\xa4\x01\xde=C0\x9d\x09k\xcbW\x9a'\x14\
-\xc4\x0aQ\x84\xe6GE\x81\x8f\x84\x023Q\x8bBI\
-TH\xe9\xa0\x81\xa8\xc5\xcd<*\x1aX $\xa1z\
-\x90\x8c\x85;4\xb4\xb2\xe0\x8c\xa4G\x06\x83\x89\xbd\x17\
-\x0c\xe1z\x04\x97AIQR\xac\xa6\x0b\xb4\x04\x83\xd6\
-\x11\xa4!\xaa:<\x8c\xe3\xc7\xb3C\xe4\x01\xfb\xf4W\
-(\x02\x04V-\x95I\x017\x94\xfaP\xa3\xd2TW\
-\x1fJ\x1a\x11\xca\x91O\xa8\xa2L\xe8=\xe8\x1a\xf2\x08\
-\x0b\xce\x0a\x82@\x84N\xa0\x04\x05\x98\xf9rS\x92\x89\
-KZ\xfb5\xc2\x98\xc9'\x98XD\x8cxbTH\
-9\xc1\x86\xdc\x8fv\xaeg\x5c@\xd0]\xeb3z)\
-\xe9)\x10\xf5\x99$\x17U$\xac\xcf\xfa\x15\x8b&e\
-\xd6w1q\x11\x177Z\x02<\xbf\xbez\xfb\xef\x8f\
-\xaf\x81\x7f\xfa\xbe=\x88\xffD\xc5HY\xdf!\xbf\xfe\
-\xbe\xf7W\xef\xee\xae\x7f\xf7\xdbu\x94\x19\x99_\xfe\xbe\
-\xbdZ\xa3\x00\x8f\xef~_\xbd^\x7f\xfd=\xf0V$\
-\x15\x00B\x9e.\xb4\xed\xd9\xb4\xfc\xab\xbb\xdbw7\x17\
-\xdd\xcc\xffs{u\xb3\x9e\xfb\xddU\xb3\xf4\xed\xfa\xaa\
-\xf9\xdf\xab\xe7\xdb/f\xf7\xdf\xce\xee\xeef?=\x96\
-VsW1\x88WRP\x1f\xb3k\x0d\xbb/\xc3\x9f\
-bHq6\xd1\x09yIa\xd7\xb3/\x9b\xdc\x94\x92\
-\xaen\xcf\xb9x6%\xd0\xd2\xfc\x99O\x18K`\xa0\
-[\x93\x89T\xd2\x81l\xc2Y4\xc8\x8dZ\x06DP\
-\xd2]b\x22^42\x93\xdbL)\xa1\xa9\x13\x8dB\
-D\x86\x8fy\x06I>\xb1(\xee)\x90g_w\xb3\
-\xd3J\x10\x81`\xcb\xb6f#@\x09g\x8f6\x13\x0b\
-\xabx\x9b)\x05)\xa8\xcdD/\x0cM\xaa\x99-_\
-T*h\x19]\xdaV^\x89\x82\x18\xc8]\x16(R\
-\xc2\xd8\xb8\x16\xd6\xcd\xac\x15\xfb\xba\x9b]\xc5\xf8r\x95\
-m\xc8\xf2,1\xad\xca\x92T\xc1.4\xa8V\x14\xc8\
-d\x1dF4)dN\xf4\x0c96\x99.E\xd2\x91\
-\x9f\x1aG\xf2\x83\xed\xf8\xf7\xb3\xb5\xf6\x15.\x12\x08(\
-K\xc6\x1e\x85\xd3!\x9f\xb3\xb5\xcd\xa4\x02\x12\xe8\x13\xd1\
-\x92\xe8-[\xf3\x82\x1e\x81\x13\x85\xe2\xe1\xa4\xb6\xaa\x17\
-\x156\xe7\x9c\xa8\x16\xcd\xb0hE\xc8\x92\x94\x0d\xa9a\
-\x09f\x0eZe\xb6w-\x9b\x17\x12\x80\x96p\x11d\
-Q'\xc0N\xf6\x97\x8f\xd9(n\xcf,\xb0\xcd\xf4\x82\
-\xa0\xfaT\x94g\x9bG\x85R \xde\xabVz\x11\x12\
-\xa7G\x01\x22\xb5\xcd\xc4\x02\xae\xf1,)\x7f\x18\x95\xbf\
-w\xc2u\xad\x05`\x5cy>\xddu\xc47\xb7\x17\x8b\
-v-qcJ\xe6\xf7\xf3\xe5\xe7\xfe\xf1\xbfUd\xef\
-\xc9\x92\x9c\xdc\xccW\x034\xde\xccC\x914\x04\xb5\x09\
-\x14\x11\xc8\xa0lm\xbb\xb3\x81\xa6L\xa6O\xc6\xdf&\
-\xd3\xcc\x82\x82\xe2\x93Xzj\xa0\xb1\x8b\x99w/\xca\
-\xcc\x8e\xef\x9by\xac\xeb\xd5\x22\x0a\x08\x05\xe4\xc70\xf3\
-\xac\xd5\xcc\xef\xd8\xbe\x9b\xda\xe1k\x864\xbd0a\xe0\
-\xba\x1d\x05/j\x88\xd4\xb5\xa3\x88^<\x11\xb8kG\
-\x91\xb4\xa8\xaa\xd1{\x06\x80\xb9\xa8Q\xc8\x9a\xb1`/\
-\x22\x12V\x0d\xe9*\x13S\x8c\xd7\x0ci\xcd\xee\x1a\xd2\
-\x9a\xbdn\xdb\x98K\x0a\xbcg\x05)KDp\xae\x1b\
-R\x93\xe2\xaa\xc4\xbanH\x0d\x8a\x03Kj\x97\x85\xb6\
-\xd5\x8dL\xad\x85u3\xbb\x86\xb4f\xaf\x1bRm\xab\
-\x1bn\xda\xb5\xa4\x86\x05\x02\xdc\xb5\x0b\x8ee\x81\x08\x00\
-\x5c\x07\xd2\xad\x84\x92\x06v-ix\x81 I\xaf\x96\
-\xb46\xe4\x9a!\xad\xd9\xebv4\xa1d\x90ht\xed\
-hH1fe\xadv\xd4\xb3\xb8\x09\xba\xad\x1b,\x97\
-\x92J\xae\x1d3\xeaT\x14\x15=\xab\x19]f\x8az\
-\xb2\xac\x9bQ$*\xa8\xce\xde5\xa3\x8f\xd9\x00H]\
-3\x8a\x98\xc5\x12\xc2\xbav\x14QJx\x8a\xbcgG\
-\x11\x0a @\xac\xd9Q\xe0b\x86h\xd5\x90VT\xd6\
-\xec\xe8\xae\xbeS~~\xbe\xd3\xcfe\xe5\xf3\xa3Zy\
-\xc4\xe2d\xc1\xfa\xf3\x9a\xf9Op%;\x11\xd9\xff\xf7\
-K\xb9\x8d\xf2\x98K\xb9\xc7\x9f\xaa\x99O[K\xd3I\
-'\xc8\x5c\x00\x80&\x04%\xdc\xa8\x95t\x85\xae\x16\xec\
-\xbc\xf6w\x0a\x85P\xa2>x\x8cg\x81\x9f\x84\xe9y\
-#vP\xff\xfa\x0d\xa7\xaf\x90\xbf\xdax\xca\xdc\xf7Z\
-\xe7\x90~V\xac\x7f\xfc\x12\xbf|\x9fUaU\xee\xe7\
-g\xfd\xfc\xe0O\xa9\x10\x1b\xfc\x0cS\xfa\xf9\xc5\x16~\
-\x10_\x02l\xf0s\xf5~~\x09\xdb\xf8\xf9\x1f7\xf8\
-=\x9a\xdc\xaa4\xfb\xac\x16\x03\xea\xd9\x88\x09\xb2\xc7y\
-#\x17\x8b\xc5\xd0\x81\x00]\xd6\xb6\xc7.wZ\xc4\xe2\
-bc\xe5\xc4\x88\xf5q\x06\x87\xecK5:\xee\xf9\x08\
-]\xd62\xe2\xf8\x95\xd1p\xa4\xf6H\x9c\xbe\xbb\xc4\xc6\
-\x8b\xdcC\xe2\xcc\x9fQb\x91<@\x01Dq\x10\x8e\
-\xe1Wm\xf7\xb0\xe6qo\xf1\x86\xdf\x8f\xda\x99L\xce\
-\xb2}g\xb2I\xbb1\x99\x8a\xb5\x1f\xef\xdb\x9e\x8c\x14\
-\xcd%\x93Cw'C\xbd\xfb\xa0\xad\xc9u\x1b#\x14\
-$P\xab\x11g.\xa8\x09\xack\xcfp\x1d\xa1z>\
-\x95fp}\x0f(\xb7\x9f\xa7\x01\x950St\x82X\
-\xdc&R2I\xc8\xc6\x9c,\xca1\xbc\x97\xc7\x0f?\
-\x03P\xa2\x10\x12*\xd6G&Z\xd4\xa2\x0b\x8feI\
-b\x88X\xdbe\xc0I\xf6)\xc0\x03\xa3\xe1\xf9\x05+\
-\x8f\xc7 :lx\xb0\xf2H\xa1\xf0.<\x89m\xad\
-\x1d\xac\xbb\xd4\xa0\xbev\xba\xbd\xee\xa1$;l\x96&\
-\x98\xc0(\xd1mPt\x01\xf8e\x8a.#D\x1f\x9c\
-s\xd5m:\x89%@\xa13]U/\xa0\xa1(u\
-\x02\x96\x05T\x03\xf2y\xde\xa0\x5c\x8c\x81\x86gcE\
-1Sr\xe2\x85\x15\x938\x17\xd3\x98L\xa3\x801\x09\
-k\xfb\x0b\x0b\xb9\x13\xa2L\xa6\x04\x85H\x9c\xac\xf9\xca\
-%\xd3\x8c\xf3\xf0I\x1b\xb1\xd3p\xd7\x918\x10D.\
-\xea\x81\xa4uo\xa1\x14e$\xad\x93X\xec\x92\xcc\xdf\
-'\x99wI\x06\x80\xb4\xa2\xe0\x19\x96\x8f\xd3[EH\
-\xf4\xc9\xd4\xb8\xb4\xe0\x19N\xa6L%\x1d\x10u\x14b\
-'5\xc5i\x85\x8dA;+\x97\xa2\x18\x18v\xb6\x11\
-2\x15P\x0c\xab\xbdN\xb8x&\x90\x1dh\x8b\xbd\xa8\
-\x98\x99\xf9q\xec\xb1\xed\xb2\xb1]\x0eV*T\xeb\xe8\
-\x14Z\x81\xb0\xaaR\x8e\xc5]\x89\xean\xd5.\xc5\xbc\
-K1\x1cD0\x85@i\x90\x22\xc3TX\x9e\xad\x9e\
-\x04\x98!\x13.\x9c@\x086A/\x0c\x09\xd6\x5cV\
-/\x96L\xe3:\xa5\x0d\x1bu\x8b<\xd8\xa8\xb7pd\
-pU\xb1\xb4\xc2\xceH\xdc\x89\x92A\x11\xf2\xea\x0cU\
-\x9aA\x15CwR\x89\xc7n\x08\xee\x84\xe1\x13+\xaa\
-J)6\x99JIAs\xc0Q:6l\xfd\x15\xf1\
-\x97\xa3c\xccG\xd61\x1a\xd61;X\xc7(J\x0a\
-%\xd55Jb\x85HS\xde\x8b\xc4rx5c\x8e\
-\xc5\xc8\x04\xe4@\x1d\xc3\x12b\xe4\xca\xc7\xd21\x18\xd6\
-1\xe0_\x07\xc7\x8a\x98\xf0.\x83\xe3\xaf\x96\xbf\x17?\
-\x1ck\xf9\x7f\xf9\xbd\x92c\x97^\xf9\xab\xe5\xef\xd31\
-\xd6SX\xfecz\x05G\xf2T\xc6\xe8\x18\x8d\xf4.\
-~\xf9N>\xed\xe4\xe4\xff:\xfd\xde\xda\x17IFL\
-\xbf\x07\xfa\xe2p\xe8e|\x94g\x8c\xfe\xe0\x88\xf0\xcd\
-\xe7-:\xc6IE\x1f\x1f\x86\x1d\x1f\x14\x1e\x83\x8e\x8e\
-\x8a\xe6\x9e\x1e\x9d\xf1O\x14F\xc13\xe2I\xc9/_\
-y O\x81\xce\xcfi.8$N\x19\xe3w-N\
-\xe1\xdc\xd9V\x07Q\x1cD\xb8\xcadU *\x0e\x80\
-\xa6c\x04\xb2a\x81\x92\x0f\x15\x88\xb2h0j\xe7\xdd\
-\xc6Y \x08\xbd6\x92[1[?!\x0a\xad\xa0\x90\
-\xd1\x18\xb9v\x18\xcd\xc3\x0e\x95+l\xe9\xe8zV\xe5\
-\x83\x22\x18\xa8TMNafJ\xc4G\xb9*\xcd(\
-\xb9h\x84E\xfeT;\x15|\x9c\xa7G\xc1E\x11C\
-\xad\xbbu\x89\x04\x11\xb3\xba\xafV\xc8\x05\xb53\x89\x84\
-\xa2\x16.8|\xf2\x06QF\xda\xd3\xfb\xff\x1d\xc0\x01\
-'S\xa2\x92\x14\x99\xb6\xfc\x8a\x9e\x181bq_p\
-\xfc\xfa\x9chO\xc4\xec\x84\xa10)\x9a\xec\x150\x91\
-\x82\x99a^\xb5\x8c:$\xf3\x0d\x92y\x87d\xf8%\
-\x14f@*\x13(\xda\xa8S\xd1 \x01\xf5\x89\x14q\
-\x0a\xf5\x98 Iaso\xb0\xc3\xc8Bi\xce\xa3\xb0\
-\x93a\xec\x98\x0f\xc4\xceKZt\xb0\xe3(\x22l\xdc\
-\x85\xae*\xda\xfa\xe5y\xbd<\xd4/\x059Z@\xa0\
-\xa0\x01\x9bf\x03\x9f\x00'!O\xb8\xb0\x87\xa1\xb5!\
-1(\x98,\x18\xcb\xd7\x9d \xaa\xe68\xb5\xa3a\xbb\
-F\xf6iE.\xba\x07\xb9\xc8\xa83\xb5\x83a\xe7\xa8\
-\xc4x\xcd\x89,\x8a\xeeI]\xd5\x09\x85\xc8\xba\xd8}\
-\x9df\xde\xa5\x19\xd4!\xcaTamu\xc8\x15(1\
-&X@\xdd\x89\xad\xf9\x16L\xc0m\x97\xc3\xa5M\x83\
-\x9cL)\x8a\x90\x1b\xe0\x18\x15\xa2\x1d\xfcM\x8e_-\
-\xd7\x07\xb1\xb3],\xd7\xaf\x0f@z\xf1\x93]\x1e\x80\
-\xfc\x8a_/~\xf4\x09/\x1d\x18a\xd7\x09v\x09\xc8\
-\xff\xaa\x17}z\x811\xe2\xa1\xcfg\x88\x9f\x1e\x1b?\
-\xfb\x84\x97K\x9c\xde\xadB9\xce3\xd7\xfaF\xa9\xee\
-K\xa3\x1e\x8d\xd6\xb7\x8b\xab7\xdf6\xf4X4\x83L\
-Wp>\xee\xc9\xca\xc4\xe7-t\xcb\xdd\x9f-]\x02\
-'\x03\xae\xea\xb6\xa4\x13\xc54\x05\xdfZ\x95\x8dWT\
-\xf5\xbfd\x8a\xe4\x83/\xa8\xc2\xc2\xe2\xa6\xabh\xd1V\
-\xa1\xeb\x1b\xaaz\xa4\xb7\xc0\xf7\xa4G\xf5\x14\xeaJ\x0f\
-\x1e\xe9(\x86\xeb\xe2\x93\x88\x07u\xc5G\x22N<\x9a\
-\xf8\x16\xdc\xf3~.&L\x94\xf1\xd2+\xf3\x91\xea\xaa\
-\xac=M\x05\x06\xc0\xaa\xe3+\xcbiG\xaa,g\xf4\
-\x00+\xb9_E\xd7\x0d\xd5\x90\xf5\xc4`\xc3U\xb0h\
-\x8aE\x8c\xd2|1]\xd9P\xa3e\x1c\xc4\x0bi\xec\
-\xea\x04\xd7\xa3\x0f\x88R\xbf\xc1o\x9e\xc7\x80\xa9\x81G\
-a\xad\xd3j\xb6,JBO\x1b/I\xb9\x00F\xdd\
-\x18\x5c\xefy\x1e\x17\xc8\xb4p\xf6\xbc\xc17F\xeeN\
-\xea\xd9\x9c\x84p\xf8N\xad\xcaw\xf6\xea\xbb\xab\x8b\x7f\
-o6\x9d?\xfc\xb5\xb9\xb8gE\x879\x14\x1d\xc1c\
-P\xac?-?;\xed\xb1\x1c\x1eyB&Ss\xa9\
-\x0f\x87\xcc\xa5\x86\x0f8\x8a;s\xd4\xd0|\xd6\xa0|\
-\xbdz\xf0\x13\xa1D\xf9\xa0\xf2P\xa2\xf5\x03@\xd6\xa6\
-\xa1S\xa2{\x14\x88\x12\xa3\x9f5c\x9b\x86X\x17D\
-\x92^\xfe\xb4E\x83\xe4\xa2M\x83\xfc\xd9E\x01\x00\xfb\
-\xcb\xa0\xfe2\x14\xda4X\x86\x10Cff\x7f\x19\xb2\
-\xa5\x0co\xd3p\x19l\xb1\xbd\x8c-\xcdl\xd2\xa6\xe1\
-2$\x07\xe4\xd8\xd2\xde\xaem\x1a.\xc3@{\xf9\xf3\
-\x96\xf6\x8eY\x9b\x86\xf9;\xe2V\x19xK{\xcf\xb8\
-M\xc3e\x04\xcaV\x9d\xe2\xfe\xf6\xbe\xbf\xba\xfe~q\
-7\x5cD\x22n/\xc2\x06_\x1f>\x5c\x86\xf0v\xa8\
-b\x0bT\x97M\x1a.C\xd1%<\xa2\xb7\x0c\xd9\xd2\
-\xe4)m\x1a.\x83I\xb7\xca!\xb4Em\xe7m\x1a\
-.C\x153s\x8b\x1c\xb2\xa5\xfbi\x9b\x86\xcb\x08Q\
-\x01\xf3\xfe2\xb6\xb4\xb9R\x9b\x06\xcb0\x08\x03\x00\xea\
-/cK\x9b\x0b\xb7i\xb8\x0cA\xdb\xaa\xbb\x8a\xfde\
-p\xb6i\xb8\x8c\xa4\xd0 \xea/\x83\xb7\x94\xc1\xc3B\
-8\xa7omp\xd5\xd1c_dDfz\x7f\x19\xbe\
-\xa5\x03\xda,fC\x9d\xa3\xe7\xdd;\xe3\x0e!\x19\xf6\
-~\xeeV\xaf\xa4T1\xeb\xac]\xa4\x92ikk\x17\
-\xa3\xb8\x93R\xdf\xb2\xc1p\xfb\xb0O\x13\x1e[\xac_\
-\xb6\xe9P\x9f&b\xdba\x18\xde\xa6\xc1v\x05I\xe9\
-\xe7O\xfd\xfc_g\x9b\x06\xf9#H\xf6\xf3\x97-:\
-\xb3h\xd30\x7fa\xc2$\xee/\xc3\xb6\x94\x91M\x1a\
-\x96\x81P\xad\x9f\x7fl\xd3\xfb6\x0d\xf3\xb7\xb4m\x83\
-B\xe4\x96v6o\xd3\xb0o\x99\xb1\x85?\x1d\x81\x7f\
-n\xf5]#\xb7\xf9\x95\xd2\xa4]|\xbe\xe0\xed8m\
-ik\xc16\x0d\x97\x91\xaa\xdb\xcb\xd86\x8f\xd06\xed\
-\xe0\x04dDj\xbf-\x05\x18m\xaf\xd5!M\x89\xfb\
-\xcb\xa0-e,\xda4\x5cFJ\xf4\xf3\xdf\xd6\xde\x97\
-m\xdaap\xd6\xed\x0e\x00lio\x876\x0d\x97\x91\
-\xbc\xdd\x07\x87\xd8R\xc6\xeb6\xed0>kl\x1d\x9f\
-\x11\xb6\x94q\xd9\xa6\xe12\x06lH\x22\x8d\xd4)<\
-U\xec\xc1#\x0b\x12\x81o\x89@\x98{\x09w\xc0c\
-\xc6!\xc2\xa8g\xcc6\x19=\x99\x07\xd82h\x98\x8d\
-6\xb8\xc8\x12\xfd\xfcc\xcb\xc0Mm\xdaa\xe2\xc8\xfd\
-\xc6\xdca\xcb\xa07\x1b\x9e\xf9\x1a\xe5\xd6\x1e\x11N[\
-\x0a\xc06\x0d{\xacJ\xb1uD\xf2-\x8d\x9c\x17m\
-\xfa\x84=V+@,u\xe9\x9dbA`\xa5\xf7<\
-V\xe6\xe8\xf5XUz\xb4_m\x8b[\xf9\xbaM\x07\
-{\xac\xbaE1)\xda\xb4\xcb\xe8\xc9\xdbF\x850\x18\
-\xd1\xae\xa3O\xb2K9\xe4\x5c\xb7\xb4\xdd\x03\xe7\xec3\
-\xa1\xf9\xee\xa7\x9c\xc5\xee\xb1\xf3\xd7\x97\x97\xba\x11\x93\x1f\
-q\xc8\x19K\x1c\x80\x06+\xec\x8e\xc6\xbc\xa93~\xa0\
-\xca=\x9ci\x9f3\xce\xe4\xe2\xa8`\x18\x1e\x02\x86\xf1\
-\xee`,..\xebK<\x07U\x83M{\xd0\x18f\
-=\xfe\x00<\x0e;\x04\x8e\x88S\x9d\x80\xc8\x09?\xe7\
-y\x80\x00\x07\xc0!@{h\xc7\xffk\xef\xda\x9a\x9c\
-8\xb2\xf4\xf3\xfaWt\xf0F\xacH\xce\xfd\x82YG\
-@\xdbLL\x84\xbd;\xb1~\xd8G\xa2\x01\xc10\x03\
-\xb4\xa3il\xe3_\xbfu\xaa$\x95J\xa8\xbaQ\xd3\
-=`\x8f%\xe3V~Yu\xf2\x9c\x93\xe7\x96\xa9K\
-\xea\xf3\xe7\x07\xfc\x1e \xc8\x8c:fI_\xa7:\xc8\
-\xae\xa2\x0e:\xc0:\xca\xbf\x0fP\x07\xc3a\xa1\xe3z\
-\xd5\xc1q\x15u\x08\x1c\xa2\x0e\xc2\x03\xd4!\x07ER\
-\xc2\xebUG\xf2U\xd4\x91zS\xb1C\xd2\xffE\xb1\
-c^\xe6\x19])\xf0\xe5\xe7\x92\xebs\xdc]\xef_\
-r\xfe.+|\xc4\xa9\xd2\xc2)\xfc\xf4b\xca\xf8!\
-e\xff\x04\xdbP\xbcJ\x9aU\xe4\x83\x5c\xe5\x04\x9e}\
-\xacm(\xce\xa4\xd9y\xd2\xd7\xe8*fy\x05u\x98\
-\xe3M\xb9\x8a9\x7f\xa9\xaeb\x91\x97\x9e\xce\x0d\xcf\x9f\
-\x07\x1c\xe8*\x96X,\x1fN\x19.s\x15K\xfe\x04\
-\xdbp\xbcJ\xcd\xe1H7e\x1b\x8e\xf2\x19K0g\
-\xbe\x8a:Xo\xaa\x04s\xf6\xc3J\xb0k\xfeIf\
-\x06\xcc\xdf\xe5O2_\xfd\xbc\x8f\xf1\x84\x04\x7f<{\
-\xac\x82_\xcbI\xaew0[\xd6\xa3~\xdc\xe4\xf6\xf8\
-\xa5I\xd8:P\x1d\xa91\x88o\xf6}8\x9bj\xe8\
-5\x89\x18\x17\x88\xe8\xb7\xae\xe3\xc0\x87pbY\xffB\
-A``P}\xab\xa7\x19\x07\xd5\x17a\xad\x05[=\
-\x0f\x93\x7fz\xa2\xe8\xd5\x15\x90\x8fgS\x81\xe7\x01\xe7\
-M\xa64\x96q\xbb\xbad\xe8\x80\xadcq\xc3\x9a\xb2\
-\x8e\x87\xe2\xa6tm\xb3k\x12\x03a^\x8e\xc0C\xe4\
-\xd0-\x11x\xc2}\x98N\xb8\xaf\xf65q\x8f\x17p\
-\xcf\xd7\xc0\xbd7J\x1e\x99\xd7j^\x17\xeft\x01\xef\
-z\x08\xef\xb6\xc5\xbb\x8c\xbcG\x03\x00\x1a\x99\xb7\xbe}\
-\xddg\xc8:\xfa\x9c\x18N\xbca\x92\xbd!b\x1an\
-xUn\xcc$\x80\x1b\x8e\x11\xb0\x83Ds\xc5\xf3x\
-\xcdGL\xe5\x90\xad\x9e-\x9f\xbf\xfd\xe6\xfex\xd6\xdb\
-\xc9\xeb\xe5\xb3\x9f_.\x7f\x99\x88\xf8\xcb\xcb7\xcfN\
-\x7f\xb9\xb3\xfe\x88\xbe\x85\xdf\xda\xd7\xbf\xfe`>\x10\xed\
-d\xb2\x93\x17\xcb\xee`\xa9\xee\x9a\xff\xba\xb5\xa7k\x95\
-A\xfbwr\x06\xbe\xdf\xbd\xec\x8e\x9c;}\xb5\xec\xe2\
-\xdc\xd3eQ\x5cw\x9cuC\xec\xc1O\x9f\xfc\xa3\x9b\
-\x82}=ON\xcf\x9e-\xcf6#\xe0\x04\xee\xb3y\
-7C\xd6?\xfa\x8e\xe2g\x0d\x0fE\xcff\xb2\x9e\x9c\
-\xbc]N\x99\xff\xed\xf4\xb4\x0b\xc1\xd4T\x89\x90u\xda\
-9|~\xbb)\x09\xa4L\xbb*\xdc\x1a5\xf5\x88\xa0\
-\xbd\xaa\xac\xd8l\xb0\xb7\xeb}\x91\xf5\x1d\x82\xef\xce\xce\
-\xbay\xbe\xf3\xea\xe4\xfd\xb2\xe3\xfc\xfb\xfa\xf3x\x95\xc0\
-\xff~\xfaK\xa9\xad\xb2\xe1\xbb\xe5\x08\x95\x8a\xdf\x8e\xe0\
-H\xab\xef\xb9\xf3\xe4\xc9\xe9\xaf\xab\xde\xad\x12\xa6\x08\x8d\
-u\xd3_\xba\xd6\xa3\xb3\xd3\xd7\x7f;[\x82\xd8\x8f\xcb\
-\xf3\xf3\x97o^\x94\x03\xd4c\xa8\x17~}_\xb7\xac\
-\xa0\xa1\x90\xa9\x1a\xe3\xa7_'\xd0\xfbm\xe8m7W\
-\x1d\x9d\xd2^33\xdf\xedx\xffA\xc7z\xbe\x00\x00\
-j\xbezp\xf9\xfa\xa7\xbd\xf8\x96\xb1\xd1x\xe9\x16*\
-#\xba\x1a\xb1\x1bp\x8d\xbd9y\xf2j9\xea\xb2\x1e\
-?\xbf|\xfb\xb2\x03\x07lp\xaa\x0f\xdd\xe9\x9b\xaf\xee\
-\xbf^\x9e\x9f<;9?Y\x9b\xd3\xba\xdd\xed\x18|\
-\xf5\x1f\xf7\xdf\xbc\xbd\xf7\xf6\xf9/\xdd\xab\xe1\xe5\xab\x97\
-O\x97o\xab56{\x17_\x17\xbc\xa5\xc4\xe9\xd7f\
-(\xc6/\xc7\x8c\xcd\xfe\xce\xbf~\xdb\x01\x8c\xe6\xc2\xc4\
-R,\xd60w7\xe3LF\xfd\xf1\xf4\xdd\xd9\xd3\xe5\
-\xc3:)\xee\xed\x15\x86,\xd7\xea\x1c\xf1\xf5\xf7\xcb\xe7\
-\xe7\xff\xd3O\xee\xa8\x98\xd50\xa7?\x9dw\xa7\xce\xfd\
-\xd6\x7f\xe5ce4\xa3\xa8\xe7'g/\x96\xe7k\xb8\
-\xe8M\x91\x92e`\xa3\xfb\x06\xc8\xf2QW\x03\x9dt\
-\xac\xfc\xed\xbf\xffB24J\x9b+Z[h\xddP\
-\xd5`7\xa3\x9d\xa3\x94\xffmfp\xf8&IM\xea\
-\xf3\x93W+\x0f\xef\xee8_\x1e\xaf\xcc\xe7Q\xff\xe8\
-\xf17\xa7?lz\xc6\xeb\xcby\xea,\xbd\x93\xa7#\
-\x95\x9e\x8b\xd2\xf2\x94\x8f\x82\x0b\x1c\x05\xbd\x82\xfchi\
-\xceJ\x8a\x7fDE\x14<o%\xd57x\xca\xfd\xb3\
-g\xcf\xef\xfd\xef\xb7\x8f\xbaCO\x9f\xde\xfb\xbf\xd3\xb3\
-\x7f\x0e\xeeX\xe8\xc9\x93\xd3w\x9d2\xba\xc8\xf5\xec\xe9\
-\xbd\xe7\xc3x/_w\x91\xfd\xee\xdb\x9f_\xfc\xe7\xaf\
-\xaf_\xdd\xbf;v\xd45\x15\xac\xea\xf65\x81\xb3\xe5\
-\xdbr\x83\xce\xa9\xff~~\xfe\xd3\xbd\xbbw\x7f\xea\x0e\
-kl\xa7g/\xba\xfb\xba\xff^\xbf\xac\x1b\xee\xfex\
-\xde\x1d\xd4\xf8\xd7\xa2;\xb8\xfd\x8a\x8f\xee\xd5\x86\xb5\xbb\
-k/\xff\xe6\xab\xafv\x16\x11\xd3c\xd5\xfc\xf1AE\
-\xf9\xe5\xe9\xbd\x14?\xacs\xa7\xab\xe5\xd9]I\xf6o\
-7\xa9\xae\xba\xb0*\xe6\xbdD\x1a\xa0\xe2\x1c%\x0c\xe3\
-\xc8)\xa5\x98\xa7\xa4\x1as\x94T\xe3\xf8AL(\x11\
-\xccSJ\x939J\x91\x0f\x1e\x1d\xf3\x94\x12\xcdRB\
-V\x9f\xa3\xf4\x90\x8f\x8f\xbf\xdd\xe1I\xe6)9\xcfR\
-\xfaV\xbe\xa3\xefrJi^\xe3\x04\x99s\x94\xbe;\
-~D\x8ftJ)\xe6)\x09\xeb\x1c\xa5G\x9d\x9a\x1e\
-M\xad\x80a\x9e\x92\x13\xcdR\xea\x1fSJ\x83\xc6w\
-\xdf\xeb\xbf\xcc1\xe20\xc7\xc8\xeb\xf6\x0ceU\xf5\xa9\
-$>\xe8d\xff\x9c#\x9b\xa1\xe8\xbc-\xc6\x838\x9e\
-\xd0\x13\x5c\xd3\x9b\x99{J\xb3\x88\xf9\xf9\xaf\xe7\x94\xa2\
-\xcf\xcf\x1a\x1b\xcf\xcf\x7f=\xa7\x94\xf2\xba\xe6_q\x96\
-\x922\x1e\xc2\x93\xf2,\xa5\x10\x99\xf7\x93\x87\x1f\xe8I\
-u\x86\x12\xce\x12\xc1\xcemyJ\xc4\xe7\x0c{f+\
-t\xdc\x0f\xb9\xf9M\x8f\xbdj:`\xd6\x8c\x0e\xd5P\
-\xb9\x8b\xf2\x94\x88\xac44\xd1\xc7\xa5\x1aBx|c\
-\xfb)\x9f\xae\x98\xbc\x06\xc58^M1\xf8\xf8\x86\xb6\
-j>Y-n\x87\xaa\x85\x93\x1frL\x89\xc4\xd5\xd4\
-B\x8fol\x17\xe80\xc5D\xc4\x83\x98\xa6\xe4\x98\x0f\
-Z\x00N\xb3\x94\x8e\xe3\xbb\xc8)%\x9d\xa7d>\x1b\
-H\x1f<|p\xfc`\x87\x92_P\x04\x89\xcfQ:\
-\xd6c;\x96)\xa5\x0b\xd2\x04\xa2\xce\x16A\x0f\xbb\xe7\
-4\xb8\xe7|\x9a`@\x9a\x0f\xee\xf5\x9cR\x9a\xd7\xb8\
-\xe0\x05\xa9\xcb\xbb\xa7M)\xcdk\x5c\xcds\x96\xd2\xb7\
-\xf5\x9cR\x9a\xd38\x1e\xe0c\x993\xeeq\xff\xc5\xfa\
-\xb2\x17lR\xbf\x1aP?M0n\xd8T\x8b}\xf3\
-\x8d\xe0\x0e\xf9\xe1\x88\xb9E\x00\x18/\xd0\x1axB\xe0\
-\xd1\xf7\xdb(f\x03\x8b\xf0\x0e\x0dn\x9a\xb6\x86\xd2\xbb\
-c\xf1C\x1a\xd2\x16T\x80\xb1v@\xb4P/@\x1b\
-`\xdf\x96\xa0,2\x88\xde\x92e\x11\xd6\x1c\x1dX:\
-:\xd5\xcf\x90\x85\xb1Pa\x85(-B[0O\x01\
-\x86\x02\x8aR\xdde\x884\xc3\xfc\x88\xee\x1c\xc0\xbfy\
-\xd3\xaeEj$\x08\xf5\xa7\xee\xaf\x7f<`\xf7\x08\xfe\
-ai<\x9c\xd8?\xbcfw\xba\xbd\xde\xea\xda\xf7\x83\
-\xd1\x82f\xe6\xeb\xf3\x1eX\x1d\x85\x17w([j0\
-s\x7f\x9e<\x13*\xcb\x9a\xcc\xb8kt\xfalY\x0b\
-\xcc.v=]=j\xc2\xfb\xc9\xbc\xfc\xd2kbj\
-N_\x0c\x16\x92,\x87\xea\x8b\x04oO\xec\xaeF\x02\
-`\x5c 7\x05a\xb6\x9a\xba\x11%l&l\xd4\xa1\
-edN\x03\xe4\x1a6\xd8\x9d\xc36\x14\xda\x08\x87\xdb\
-\xc4d\x00:\xbc\x07 2\x8a:\x124.\xc3\xf36\
-P)\xc0!\x0b \xc4\xbe\x99\xd4\xdb%\xd0V\x0b\xbd\
-\xb8\x18\xda\x88\xba\x9f\xe3\x11-c\x9b\xf8\x9ci\xca\xc1\
-38z\xacl\xebM\x1b\x97\x08\xf5\x07\xa3\x1b\x7f\xd5\
-F\xc4\x96\x99=\xe2\xc50\x94v\x90\x8e\x8e\xab\xed\xb0\
-ik\x94\xa8C\x0b\x9bG\x16\xa2akD\xb0\x1c+\
-\xa9%\xca\xc2c\xa5\xa8\xe4F\x91\xd5&\xc4j\xe9\xc2\
-{\xb2\x9b\xd7\x83\x8e\x92K\xc1\xba\xcb\xde\xaa\xbd\xeb\x85\
-e5\x83\x91LKg\x22\xd7\xdb\x1f\xa7\xb0\x19\x9di\
-n\xeb,+\x85\xe3\xc2[fh\x19O\x13\x83 Y\
-#\xd1\x80\x90\xdd\x17\xd1\x84\x93\xd9\xb6\x91\x0c\x18D\xf1\
-\x96.@\xb1@\x88\xc6\x8a\x14\x1d\xa9\x09\x18\xc9\xa4E\
-\x1dY\x5c;(Kz\xec\x90Tr\xc5\x0dR\x8a\xb1\
-\x16\x89.\xde\x832\xcc\x13J\xa2\xe2\x80T[\x00\xcd\
-\xa8oSjra\x1a\xec =\x86H\xcc\xa5u\xa8\
-!\x89k~ j\xd2\x0b\xd24\xb2\x9a\x9aH\xb7\x02\
-\xbc\x1a&J\xce\x9b&\x87\x83\xea@\xc3\xccc\x91\x0d\
-P\xb2\xb4\xb4F\xa2\x89sZ\x14@\x94\xcc\x1d\x02\xcc\
-\xc8t\x14\xd9,\x12bD\xbe\x9f*\xfb\x80\x19\xa7\xdb\
-;Q\xa47\x80\x17\x93hv\xd9\x9b\xfa\xeb\xec\x87\xe9\
-<f\xbf\xd1\x10V\x06\xbe\x08[\xd9u\x01f=\xc0\
-le\xbe\xc0\xbaH>\x8aX$w\x17T\x87X\x8f\
-t\xb8T\xa3\x7fe\xd5WS\x8cu\xc7\x91G\xdd_\
-i\xb0\xc8USch\x0e\xfe4\x1dx\xd0\xca4D\
-XA\xfbC.~=\xaam\x9a\xf2\x89\x12\xcbQ>\
-VT\x8f,\xa0\xf7ao\x22\xb2H\xe9E\x95A\xd4\
-H\xef\x11+Q\xfbln\x83\xa8%\x0ch\xddQ\xb2\
-\x11b\xc9V\xe4\xae \xaa\xf3\x15E\xe5CD5\xe5\
-\x02X\xbcD\x0d\xf0Ej/\xaa\x968%\xfc\x80\xd4\
-\xff\xab\xd1\xbf\xb2\xbe\xafD\xad;\xfaY\x15\x1ffU\
-\xf9\x0a\xa2\x06\x5cQT\x9d\x11u\x87\x85\xe3] \xa1\
-\x0dqG\x8f\xb2\xa6\xac.A\xc0\x0e\xcch\xba\xcb\xf0\
-\xae\xe2v\x81\x8cF\xbd\xadG\xactSq\xce:p\
-w\xdcis\x8f&\x10pW\x15\x17\x88\xef\x83\xf8w\
-_\xec\xad`Yp;\xba\xbb-\x08*>\xd7\xdf\xbd\
-\x01\xe7\xcd\xe9\x9b\xe5\x10\x5c\xce\xde\xbdZ\xde[\xfe\xbc\
-\xec\x92H}\xf0\xf3\xec\xf4\x9f\xcbM\x0d34\x87w\
-\xb1\xef\xe1O\xbf\xae\x81\xe2\xae{\xf7\xf3\xde\x93w\xe7\
-\xe7\xdb\xd8?\xba\xdf\x95\xea~_\xaa+nV\xe84\
-t\x15\xf7\x93\x82\xbc\xd6\xfes\xa1\x8c\xfa\xbc\xe0\x0b\xe4\
-\xfa\xab\xd0\x99\xfag\xa8\xf7\xae\xbb>\xfe\xf7]g\x8c\
-\xc6j\xaa\xa3+\xefv\xf9\xbfME|\x03\xcb\x89\xcf\
-\xb4\xfc\xba\xbc\x98as\xbd\xfd\xc7\xaa\xd9\xa7f\x9b7\
-\xa12\xdf\xad\xff\xfe\xac\xe0\xbf\xa8\x0a~\x9c\x7f\xc3\xcb\
-\xe7\xbf\x1e\x9b\x02~\xcc\x81\xc6\xb7>\xbe\xa0\x1f\xed\xe7\
-\xc0\x02\x8a\xcd\xb3w\xc0]\xc6\xf5\xd6\x97\xb9\x1c(M\
-]]\xd8\xc0\xbd\xc2\xfa\xad\xcf\xb1 \xb8qay\xaf\
-\xb0y\xebK\x5c\x12\xcc\x0b{\x81\x80\xbaO@\xc7[\
-\xbf\xcb\x85\xc0\xa4\x8e\xff\xfc\x95\xf9e\x0b\x87\xa9\xcey\
-\x87\xfb\xb1\x93\x0c\xa8\xae\x1f\x08\x916&3\xb3E\xc5\
-\xcfI\xb3\xc8~\x09\xab\x91\xaf\xb6\xde\x1c\xe8?%\xba\
-\xf3\xe5\x90W'O\x96\xafV\x9f\x1c=\xaa\xbe}u\
-\xd5\x9d2*\xa8g\xd5\x0f\xdcx\xd1\x07\xec\xa8\xe5\xea\
-\x10\xe9\xf7\xdf\x86\xfdMU\x8aV\xb2\xad\xf2\x0b\x9b\xa3\
-\x98\x84,\xef\xd0d\xe7\x86X\xf4\xb2\xc0\x9f\xa0dL\
-\xf8\xf5T\x9d\xa3\xc0\x17h\xd78v\xb5[\x14M3\
-\xd3>J\xc7g\xf5\xd1\xc85\xda+\xfc\xd5\xcb\xee\xcf\
-=Yc\xcfN\xba\xcf:\x9f\x9d\x9d\xbc\x1fX\x1b\xd0\
-\x8b\xabYe\xbe=\xf5\xef\xf0\x16U\x80\xd0\x10\x9a\x00\
-R\xd2\xbbf\x03\xb2d\xad\xd8\x8cn\x08X\x183\xa7\
-\x0f\xf5}5\xc3\xb5\xac\xb0Z\x06\xd5\xeah\x904U\
-@\x91\x85a3\x16\xa4\xec\xb0\x85Q\xf3\x100\xee\x1b\
-\x15$\xea\xaf6BM\xf6\xed\xbb\xac\xb1i8\xae\xe8\
-zC\xaaQ\x861Q\xab9\xa4\x0c\x07H\xe3\x0e\xb3\
-\x06aX\xb9\xb31\x93\xb0\xf7Xy\x8eV\xd9\xe1\x18\
->\xdc\xaa\xc9=\x8f\x89\x0dA\xc1\xeaBiINZ\
-E-\x00\x98P\x87qS\x0e\xc2\xa3\xacF\xe7h\xf5\
-\xa2\x88#\xe9\xd0\xf0h`C\xa3\xb7P\x1b\x84\xaf\xd1\
-(\x13\xd8\x17\xae-\x91\xc3K\xa7f\xae\xc6\x059\xa7\
-+\x17\xf3\x14H\xfd\x8d\x026\x08TiO\x92\xb9J\
--pr\x89Z\x9ef&k\xd4\x80b\xe8U\x0e\xc8\
-\x22\xa0\xf4Qu\x1d7\x1b\xe6\x80e \xb40\x19\xfa\
-HV\xecVC\xaa\xfc\x1c\x89I\xf6\xf2\x81o\x06\xb5\
-\x85B\xaf\x87m\xe6\x14\xd7\x0a\xdb\x08\xb1P\xea\xf5*\
-6\x8aZ\xd8\xa0\xff\x84\xd6\xdf\xb5Q\x93dK\xf6U\
-\x83\xb3Y\xac\x1bX\xb1}P\xb9\xa0/j\x16\xd0Q\
-\xa9\xa6F5\x8c:\x8c[x\x04a\xd1\xf5\x9e#\xe4\
-a\xf6\x0b\x01\xe0p\xee\x8d\xd2\x11\xa3&?\xd5\x99\xca\
- Z\x9a\x91\xf3h\xdb{\xf6\x89X17\xe0\xf9%\
-'\xb1v-tv\xaeS.\x1a\xe4\xd0q\x07\x1b\x22\
-\x06\x92\xdc>4\xf9\x12\x1b\xdc\xfe\xe8XR\xde=\x0d\
-$\xf1/\x09 \xd3@QN\xe3 Y\xe5zH\xaa\
-\xac\xd6\x0e\xa8\xa8\xde\x17\xfa\xc8i\x89e\x19u\xa1\x16\
-\xc6\x22\x8e\xa9\xbdk\xba(vP\x92+A\xbf'\xe0\
-\xe5\x10\x84}\xf40\x16\xc0\xb2S\xb4f\x04\x99T\x01\
-D\x14\xdd\xec\x08u\x08\xe7\x5cq\xc4\x12$'\x98\x94\
-\x1d1y\xdd=\xa2\xda\x18\x01\xd5\xb7)ZS\x89\x08\
-\xdf\x1e\xdb\x1b\x9b\x19\x97en\xd8D\xcc\x06\xce\xc8\x83\
-S{\x04K.\x90\xa0!\x84D)\xc3!\x01{\x88\
-\xd2@\xfa\xd5\xad (\xf77\xab\x07\x944)\xc5\x0f\
-B\xa1\xd1\xc2\xa0\x0a`k@.\xa4\x1dV\x83s\xac\
-03\xf3>P9X\xd4jmDK\xd1\x09\x046\
-AC\x9b \x80\xc4\x04\xf5l(\xe1\xa8\x13T\xa2\x85\
-brN\xd1\x8au\x8cN\x13\x94\xbd\x05\x82\x88OP\
-\xa4F\xc9AYr\x15\xeahT\xee\x86\xe4lz\x94\
-\xda\x14\x1c4\x16X\xfe\x19\xa1Z\xf2kbT\x81\xdb\
-0-Hu\xd8107G+\x13\x09\xf14\xaah\
- \x22fe_\x12F\x94\xfbL\xee\xb7\xa3\x1f*B\
-\x19\xf41\x82\xb0\x91\x04A\xc94\xa2BMTSK\
-&\x8b\x96\x92\x01\xb2\x85\x1e\x17J\xe9\x06\xba\x85\x9a7\
-wR\x89\xc2\x92\x8d\x93Gl\xa2\xa7\x11\x1d\xf5\x7f\xbc\
-\x8dZ\x03\xb0\xa0\x18\xc7)L\xc2\xc9|\xe4h\xc4\xb6\
-\xb9\x9fA\x11\xac\xb9\x92\xf4\x097\x1a\x91\xa0\xe0\x96\xe5\
-\x7f\xbfO+\xfb\xc2\x1e\x03]\xbe.\x0a\xfdz\xf6\x0d\
-46\xfa\x92C\xd7|\xedc\x81\x97\xd4>\xae\xa99\
-\xd4>\x12\xe6\xa5R\x06\x02\x95\xc2(8%\xaa\x0e1\
-5\xd2\x82\x5cWz'-\xa5{\xac2_%\xa0\x9e\
-\xb0V\xd7PpD@jT\xa63\x0f\x8f,L,\
-I\xfbL\xe7I\xa8yC\x99\xce\x14o0\xd3\xcd\x17\
-\xcd\xf3\xc9O\xfeL~\x7f&\xbf\x9bI~!\xcd,\
-\xc5\xae7#\x16\xda\x81\x98\x7f\xe6\xc9\x7f\x93<\xa9\x90\
-c\xac\xdb\xed\xb4\xd0\x9d]2\x1c3\x89\xe3\x87\x99\xc4\
-\xe1\xc3Lb\xb9\x93I\x1cv3\x89\xebn&q\xfd\
-0\x93\xb8~\x98I\x5cv3\x89\xcb\x07\x99\xc4\xe9\xc3\
-L\xe28f\x92\xd9\x18\xcf\x17\xd6\x07\xf6\xbb\xaf\x0f.\
-\xcf\x93w\xc6D\x89\xd8\xdc\x16H\xd1\x22\x5c\xf8*\x8b\
-\xc18Lc\xd7\xbdO9\xda1I\x93\x95\xb7\x80s\
-y\x8b7\xab\xcd7\x17a\xce\x0d\x00\xd1H5\xb0\x03\
-\xa6\xb7\xecq%b\xe2\x02/|'\xe7\xf2\xba\xe2\x22\
-\x93s\xf8}\x9a\x9c\x13\xed\x94\xa4\x88\xadwx\xcd\xf5\
-6\xca\xf6\xe6Z\xacv\xc9pks-\xc6\xbd\xb5z\
--\xfde\xd0?p\xdca\x1bo\xdd\xdaa\xcb\xed\x0d\
-6\xe4\x06\x8c\xd4!\x9b]\x1b\xd4\x86j\x05mv\x93\
-\xa6\xfc\xed\x0b\x9c\x0e\xfa)^\x94\xcdBh\xde\x8b.\
-0\x02\xfaR\xbch\xd7i\xe6\xdcj\xf4\xa2\x01\xf0h\
-\xae\x98\xa1k@\xb22\x14\x1a\xedz\xe2\xfe\xa5\x1d\xc1\
-\x9c\xd6\x0e\xde\xc0\xde\x1f\xa8\x5cn\xefh\xad\x01\x8a\xb3\
-\xfa\x15\xb57\xef\x1a\x9c6u\x8da\xdb\xb0\xf6:\xa9\
-\xdf\xe5\x9c\xee\xce\xc2\xf0Wb\xd5a{\x15\x94\xf2\xd1\
-\xa6\x09-\x093E\x17w\xb21\xd8P\x05\xb6L\x12\
-\xb2\xbc=_\x1bD\xea'\xf2}Csg\x9fa\xee\
->]\xd1\x87gS\x8f/9\x0e(c\x09h\xdc\x12\
-\xd9\xb8\xf6\xc1\x19\x1b\x93*\xf5\x8b9w\x12\xa7#\x8e\
-\xa6\x12\xea\xd1\x87pN\x009\x12k\xa0\x89\xe6\x1b\x8c\
-\xab\x9e\xc6\xa6NA\xb8\x85\xba\xb6d \xcc\x1a\x87\x0c\
-3t`F\xb3%\x84\x13_-\xc2D\xfa\xe1\xf3\x11\
-\xf0\xc5\xceGXs\xac\x9cG\xd0\xd8\x83p\x1b\x93h\
-!\xe2\x93\xeb4\x1b\x1bR\xe4P\x8a\x130\xc1\xb0.\
-\xb7\x00\x8f\x9a\x0c0T\xebS\xa6\x81\x22\xf3\x91hs\
-\xa3D\x1f\xb1\x9a\xf4h\xc6(l[(W\x0eL\xd7\
-\x1e\xc3 \xef\x98$m\x14\xe1Z3\xc9\xa9\x80\xf2\x07\
-\xb0\xa3D\xb8\x82\x1d\xd1\xef\xcc\xaf)\x1a\x12e`_\
-?\x99\x1by\xcdQ\x88\x93da\x96AaG\xac\xcd\
-\xd4\xb5\x90$\x02\x8b\x9aKjh)\xe2\x85:\x889\
-\x17=W\xa0,\x885\x89\xcb8\xa4\x0f\xa5\xfd\xb8B\
-\x22\xb1Z\xe1z\x14Je\x8e\xe9\xa0\x13T\xb29$\
-jNP\x83\x06J\x1e}\x01IM\x18\xb2\x0c\x11\x1b\
-9\x93\xe2\x91{\x0b\xc2@*sgB3\xe9\x17\x90\
-\x988\xc8\x8d\xac\x0e\xabO\x88\xa0B\x9a\xd4\x95\x14\x0a\
-)G\xc1\x0dB\x98\xa2\xa7\x08FAW\xb0\xa4\xa3\x1f\
-\xf6+\xcb\x9b\x00)p\x99.\x90\xa8\xea\x11gC'\
-\xd5,\x0c\x01\x1d\xfdH\xa0\xa5:\x04\xf7\x98\x92Z\xef\
-6\xd9\xaa\x97\xa2PH\x87\xa4\x81\x22*Y\x8f1\xb8\
-\xe9\x9e\x91\xf7Zvn>\xcbQ\xff\xea\x87`\xbe\xf9\
-\x7f\xaf\xd7Z\xe4TM\x01\x00\
-\x00\x00\x17\x91\
-\x1f\
-\x8b\x08\x00\x00\x00\x00\x00\x00\x00\xed=io\xe3F\x96\
-\xdf\xfbWp\x9d/i\xacH\xd5}\xb8\x8f\x01f\x82\
-\x04\x016X`&\xc1\xcc\xb7\x80\x96([\x1bY2\
-$\xb9-\xf7\xaf\xdf\xf7\x8aW\x91,Q\x94,\xbb\x03\
-\xc7v:\xa6\xaa^]\xaf\xde\xcd\xaa\xa7\x8f\x7f\xdb\xdd\
-.\xa2/\xd9z3_-?]\xd0\x84\x5cD\xd9r\
-\xb2\x9a\xce\x97\xd7\x9f.~\xfb\xf5\xc7\xd8\x5cD\x9bm\
-\xba\x9c\xa6\x8b\xd52\xfbt\xb1\x5c]\xfc\xed\xf3\xbb\x8f\
-\xff\x15\xc7\xd1?\xd6Y\xba\xcd\xa6\xd1\xc3|{\x13\xfd\
-\xbc\xfcc3I\xef\xb2\xe8\xfb\x9b\xed\xf6\xeer<~\
-xxH\xe6Ea\xb2Z_\x8f\xdfGq\xfc\xf9\xdd\
-\xbb\x8f\x9b/\xd7\xef\xa2(\x82q\x97\x9b\xcb\xe9\xe4\xd3\
-E\xd1\xe0\xee~\xbdp\x80\xd3\xc98[d\xb7\xd9r\
-\xbb\x19\xd3\x84\x8e/j\xf0I\x0d>\xc1\xd1\xe7_\xb2\
-\xc9\xea\xf6v\xb5\xdc\xb8\x96\xcb\xcdw\x1e\xf0z:\xab\
-\xa0q6\x0f\xdc\x01Qk\xed\x98\xb01c1@\xc4\
-\x9b\xc7\xe56\xdd\xc5\xcd\xa60\xc7PSF\x08\x19C\
-]\x0d9\x0c\xear\xb7\x00T\xec\x9d\x8c\xab\xf5G\x07\
-\xf4\xdf\xc1\xbf\xaaAY\x90lV\xf7\xebI6\x83\x96\
-Y\xb2\xcc\xb6\xe3\x1f~\xfd\xa1\xaa\x8cI2\xddN\xbd\
-nJ\xec7\xc6ml\xc92\xbd\xcd6w\xe9$\xdb\
-\x8c\xcbr\xd7\xfea>\xdd\xde\x0050\xe3>\xded\
-\xf3\xeb\x9bm\xfdy>\xfdt\x01\xeb\xe3\x82\xe4\x9f\xcb\
-\x19\x5cVtD\x12\xcer\xd0\xa2[\xbfJ\x98\x84F\
-k\xab\x15q \x0d\xe2kt7]Mp\x86\x9f.\
-\xbe\xaeV\xb7\xf1\xea~\x9b\xc0\xa8_\x9b\xfdB\xe9\xdd\
-\xfd\xf6\xf7l\xb7\xcd\x96y7\xb00o\x95\xae\xda\xb5\
-K\x1aK\xac:\xc8vw\xab\xf56\x9e\xcd\x17Y>\
-\xd8\xf8fu\x9b\x8d\xef\xe6KX\xf6z\x05\x0f\x93\xcd\
-x\xb5{\xbc\xce\x96\xf1|\x02\x946\x86v\x8b\xf4j\
-\x91\x8d\xd3\xc9v\xee\x0an\xd3\xc5b,\xccN\x98q\
-5\xd5\xbb\xe5up\xa0\xdd\xf4\x0e6\x96r\x19\xac}\
-\xacj?C\xf5\xc7i6\xdb X\x8es\xfc\xc4\x05\
-%\xae\x0ej\x81l\xb2t\xfd\xd3:\x9d\xce\x81Yr\
-8\xaf\xcb\xc9j\xb1\xc8&\xb0o\xe9\xe2!}\xdc\x5c\
-T\x00\xd0U\xb3)\xb7T\x15\x9dB\xb7\x9b\xed\xea\xae\
-\x84\x85\xdd\xd8>.\x00+X\x18C\x8f\xab\xf5\xe5w\
-\xf4\x8aI\xce?\xb8\xa2\x15\x90\xcf|\xfbxI?\x5c\
-\xd4mV\xb3\xd9&\x83\x81\x89W\xe6\x88\x06Z\xc0X\
- R\xc6O\x1b\x8d\x84F\xa3\xc1\xd1\x18\xa9F\xfb8\
-n.\xfbiht\x5c{y\xb3\xce@\xca|\xf7\x9f\
-_\xfe\xe7\xe7\x1f~\xb7\xbf\xc7\xaa\x07\xcd\xccP#\xaa\
-\xfa\xeb\xa2\xf4\xb7\xe5|\x0br\xe4~\x93\xad\xff\x85\xbc\
-\xf8\xbf\xcb\xdf6Y\x07\xea\xd7u\xba\xdc\x00\xe3\xdf~\
-\xba\xd8\xe2\xe3\x02D\xef\xf7\xcc&Vq%\xed(\x06\
-\x92I\x94b\xf2}=?\x0a(\x11\x168\xd1\xaa\x1a\
-5\x8fPjd\xc2\xb9\x10\xa4\x9e\xcb\x8e\x01\xac\x14\xd8\
-\x83\xf1`\x99\x0f[aq\x0f\xbe`\xb9\x01,\x0c[\
-%\xce\xd5\x8a\x84\x0bn\x08o\xcc\x95\x12\x06\xc5B7\
-\xa6jT\x22\xb9\x94\xd66\xa6\x0a\x1dHD\x86\xd9G\
-\xca!\xb2\x0c\x10\x9c\x9c\xe0o\x0fy\x97\xc4\xa5X,\
-\xf7\x93r\x09\xa5\x85f\xf1\x81!\xb3\xd9\x8cd\xb3!\
-\x1c\x95p\xca\xadPz\xd8\xc0$\xa6\xfd\x03\xa7iz\
-\x95\xdaA\x03k\x06RB)\xbb\x7f\xe0\x10#\x86\xf0\
-\x0b['\xe9\x10\xfc\x0a\x0f\xbf\x7f\x06\xe6=\x96\xac\x03\
-\xcc{\x9bn\xd7\xf3\xdd\xf74\xb1\xf8c\xe8\x88\xc0\xaf\
-\xca?Y\x09|\xccXB8cz\x14+N\x81!\
--\xb3/\xc0\xd38\xd1t\xf1,hlv\x8dh4\
-gC#\xe0\xca\xfd\x18\x87\xc6\xe2\x13\x05,\x0a\x05\xeb\
-\xe6\x14\x1f\xa9\x86\xd5r]#q\xb2C$\x22\x00\xf3\
-$\xc8\xe4\x11\x0d\x1c\x82]\xd0\xbat\x16\x84\x9d\x05a\
-\xd7\x9f.\x10\xb1`\xf9\x1c\x12\x95G\xa1\xb5\xd9\x87\x16\
-\x96\xf5S\xa9%OG\xef\x1e\x15c\x90\xf0(o\xd3\
-\xa3N(Umj\xd4Fs\xd3\xa6F\x93hi\x0c\
-oP#\x85!\x18\xb7\x034\xcc\xf1\x86\x8d\xc3\xd6p\
-Sc\xe6~N4l`,q\x94a\x13\x1am\xb0\
-a\x03\xa3\xa9C\xb2\xf1\x1cL\xdd\xc2'\xa5\xa6\x9f\xb9\
-=\xea\x1cF}\xc8\x8dF'\xb2\xc1\x88`\xcc\x18-\
-\x89'\xcd\x90\x11\x8dM\x8c\xa0\x86\xcb\x06#\x828\x94\
-Z[\xdb`D\x0a\xa0\x9a1\xd2\x9dMW\x88\x10\x90\
-\xbeT\x12.\xc5\x08|(\xca\x09\x98\x1b\x84\x8f\x8aG\
-\x94-\x08 \xa4\x02i\xc2\x13\xcb\x15e\x9c\x8f(\x05\
-\x8dH@\xb6\xbe\x1fb\x19\x05\xd08\x9cP\xa6|6\
-\x19D(A\xb2\xa4\x9e\x05\xbc\xdfV(}\xbbV\x87\
-\x89\x94 E\x89\xc7\xc8\xc1\x19^\xa539k\x99L\
-$a\x80G`w\xf6a\xd0\xf8*8\xbe\x96R1\
-\xea\xedcp|\xc3\xafdv\xd5\x1e_X\xb0&-\
-\xeb\x1b?h\x9e\xe8\xd9D\xb5\xfb\xd2F\x08\xc3\xb4\x19\
-\xcc\x9f\xa0\xc8\xbf\x01\x7f\xc2\x0c{\xf8S3o#\x9d\
-\x1a\xd4 \xbbA\xb3\xd1\x06\xf7\x09\x96(\xa9%k0\
-_\x17t\x16\x02\x05\xde\xe3,\x91\x94i\xd5uuB\
-v\x90\x06\xfa\xe29\xbbY\x0e\x8c!\xb4\xe3B\x0b\x9e\
-\x00\xb0\xdc\xc8\x01X\xc58\x9aF\x14\x14\x856\xf0\xa8\
-$\x10&h\xf6\xf7\x03\x85\xcd3h\x16\xc4\xf4p\xa2\
-\xca\xcd\x94S5\x0b3=,<p\xb4\xe1\x9a\x85\x99\
-\x83\x94;\x9b/\xb6\xd9\xba\xa2\x19\x1c5\x9e/\xa1\xe8\
-n\x05V\xc3|\xb5\x8cs\x08\xd8\x89\xcd?\x7f\xfa\xfb\
-\xc5Q\x88\xce\x9b\xc24\xbc\x98\xc4,\xfb)\xbd\xdfl\
-\xe6\xe9\xf2\xef\x8b\xfb\xb57\xe9C\x1d\x22~\xa6?d\
-_\xe6nZ(Q\x94\xd2(\x7fuk\xe9\xcd\x11`\
-t\xe3!!\x9f\xd2\xb3\xb1-\xf8\xbd*\xe6\x9e\x93\x18\
-`]\x11\xd0e'{\x1f\xce\x5c\xa6h\xbf\x0aC\x14\
-<\x03\xcb\x81\x024\xc0\x5c\xa2e([0\x00\xad\xa6\
-MC\xd9\xaa\xc4(#\xa9l\x8a\x88\x0e\xec,\x08\xdb\
-\x90\x11\xe7g\xcd\x0a\x9b/d\xf8a\xd4\x22\x96q\x8f\
-c|n\xe3\x0f\x9d7\x18\xf1`d\xeb%\xd8T\xd0\
-\xe7`Sp\xe5\x8c\xe5\x82\x9a~.\x15t/\x97\x9e\
-/0@\xc9\xef\xb1\xe9\xa17\xb4z\x8f\x0d\xeb9\xc7\
-\x09\xb4\x98\x12\xc6c\x0ct\x9d8O81\xcc3r\
-\x9d\xebdMB\x0d#\x1e,s\x06\xb1\x92F\x0d\x0f\
-\xcd5\x97rDl\xae9G\xea\x05\xe9\xcax\x9cQ\
-\xd2\x93Pe<./}Z<\xeeG\xf7\x13\x8a\x0f\
-\xd9X==\x1a%\xc1~\xe1\x81\xde5=\x1c}\xfa\
-6\xaa\x80\x1d\xedd\x9dG\x15<\xaf\xb1\xf8\x9cA\x13\
-\x22\xa8\x17\x09\x0d\xb3\xf0\xb1\xc1\xbd\xcd\xdd:K\xa7\xbf\
-d\xdb\x9b\x15nQ6\xc3Y5\x19\xdc\xf0D\x11\x22\
-\x9b\xb1\x11@\x0d\xe1\xe0D6\x19\x1cX;\xe1hy\
-\xf9\x1c\x04\xd60\x9a+'8\x98\xf9\x82_*\xf2A\
-\x04\x8f{b\x1f\x1e\x9c\x8de\xa8\xd3\x84\xe6\x01P\xd5\
-\xcf\xab{\x94\xe6\xcb\x05]\xc0%\x01\xb4\xfe\x09\xb4.\
-\xd3\xe2Y\x8ccj\x19\x15D\xb1\xd6\xe2\xdb\xc6\xb1V\
-\x7f.\xe3\x98\xdb\xf3\xc5\x94\xff\x94\xc6\xf1\x8bXr\xec\
-\x99hJ\x0a\x05\xfe\x94\xed\xa7)\xc1\xf6\xd2\xd4\xf9\x15\
-B\x90\xa6\x9a J\x883\xbc\xab\xad\xc2\x1c\x84\x81&\
-\xe0\x18K\x04\x85\x00\xea\xd1(GVDr\xc2\xb4A\
-r\x93DK%G\xb1P\x09W`\xd8b\xc4\x9da\
-\xc0\x1dTf+\xe2Nh\x229\x15\x0d\xb5BM\xa2\
-@\x89h\xdaP+]P(\x94\x00J\xa1[v\xde\
-\xf7?-\x15\xc4)\xebeY\xcf\xb6G^\xd2\xf8J\
-\x0c\xd4p\x93\x97\x04\xe2\x881B\x1b\xbc\xd4\x85\x9d\x05\
-a\xeb72\xc36\xf2\x19\xc2D\x88\x85\x97R\xc3\xb8\
-\xcf/\xa7\x0b9\xfd\x16/ \x0eQ\x15;\xe5\xfd\xc3\
-\xf3\xd2\xde\xd1\xd6\x1b7V\xc7:\xe6\xfb\x08\xa7\x0a\x03\
-\x18kc\xd1x\xc5?\xd0\xa5\x9aM\xf0\xb7Eg\xc7\
-\xd1\x8e\xc1\xdfv\x0f!\xd3N2\x06\x0e\x96n{\xf2\
-\xf9\xe9\x1c\x02NV\xac\x0f\x9b\x8e\x00\xc9c\xb6\xc7x\
-\xd4\x8cs\xad\xa5=\xb0f\x92\xcd\x02g,\x86\x0c-\
-]x'4\xb4\xb1\x84\x09\xdf\xcf\x09\x1f\xef\xe0\x19\xcd\
-\xba'\x1e\x86\x0c\xada\x83CoF\x0e\x8c8\xc9&\
-W\x93\xab}\x1b|\xdcY\x8a\xae\xb7\xc4\x81\xfa\x0fL\
-\x9d\x09r\x02]\x12\xab\xcc){\x04\xa3\xf1\xe3\x91\x94\
-f\x93Y\x87\x0b>\x9c\x0bKBR\x036\x86>\x84\
-)!\x19\x89i\x98\xb4\x0f\x90U\x96\xc1>?\x89\x8b\
-\x05\xfe\x0e\xe1b\x91\xbfS\x08i\x02i\x15\x8a!~\
-x\x9b`\xa9,\xb6q\xe8M\xe3\x81\xad\x9a\xa9\x99\x9c\
-\xc9\xf3\xd0\xf3S\xecFK\xfb\xce\x08\xe1\xcb\xc6\x86\xa5\
-\xa68*\x0b?t\xefB|x\x22E*\xaf\x14M\
-\xb5\x00,\xcbc&j\xf0\x89\x99g\x8c\xa1\x1c\x5c\xba\
-\x17\xb1\x1c\x1eX|>\x04\xeds\xec\xf0\xb5\xb0;z\
-%\xd0\xcb3\x18_1D\x1f~\x89\xff\xac\xa8\xb3G\
-\xa2\xae\xef0\x03\xe3D+ \xc4\xf6jc)\x13j\
-(\xd4\x8e\x18b\x12\x96m\xde?\xd3f|Kd\x0a\
-\xfe\x5c\xc8\xa4\xc5i\xb3\x06.1l\x07\x980\xf4U\
-\xe2R\xbf(.\xe3\x22\xe0I^%2%}v.\
-\xe7\x0dlZ\x89\xe7J\xa9}}\xc8\x14D\x9e\xf2\xc6\
-\xed\x95,\xfd\x8c\xc7\x90{U\xe2+D\xdd\xf9\xc2\xc4\
-\x7fuE+\x88:\xc3A\xe37E[\xe0\xf2|o\
-t\xff\xea\x8a\x16\x90y\xbe\xc0\xfd\x9b\xa2Uo\xda\xe6\
-d\xd4\xbdi\x9b\xf3!S\xbfi\x9b\xf3\xe1\xf2M\xdb\
-\x9c\x11\x99o\xda\xe6\x8c\xc8|\xb6\x80\x83\x87L\xa1\x12\
-*\x99\xb2|\xe4.B\xbeN\x1e7/ /kD\
-\xc6\x94\xe3\xcb\xce\xd7\xc9\xe1\xe6\x05\x22\x8a\x1e*\xad\x82\
-RA({\x95\xb8<\x9f9\xb9\xdf&\xf2\x90\xa9\x11\
-?\x8a\xda\xd7\x89\xcc\xf3\xbd7\xe8\xd8\xe61\x1e\x1d\x02\
-\xe98\x8a\x0d\xae\x14P\xc8_#\x0a\xcfx\x94\x9a$\
-\xdc\x08E%7m\x5c\xeaDZ\xa6\xa5\x10#\x87\x1e\
-\xf2:5\xce\x19\xcf`\x0eAeI\x97\xe65\x1aB\
-\x94\x9c\xd3\xaa\xdc\x87Lf\x12\xcb$\xfc\xbcf\xba\xa4\
-\xe4\x9c*\xe70*_9]>\xff\xab\xea\xbf\x8c\xb7\
-C\xe99\x95\xcf_\xdc\x0f\xa7\xf4\xd8\xf3'o\x01\xa2\
-\xfd\xb8|\x8b\x5c\x9e\x0f\x99\xec|\xef\xfdi\xd2\xc5\xa2\
-I$\xe7\x94Z\xf3\xaa\x83\xe9\x94\xfde_\xf8S~\
-,\x01\xbd\xd8\xc9\xba:O\x14\xe8\x16w\xbfiD\xbe\
-\xe5Q:\xc0\xd5\xf9b5!f\xa3\x0c\xd9\x8dP\xf6\
-R\xcc\xf6q\x8cY?\xddS\x95\x17\x15\xf3\x94N\xbf\
-\xcc\xb3\x87w\x15\x1a\xae\xd2jIw\xe9u\xe6N\xcd\
-\x02\xf2\xf2\x1b\x1eE\xc5\xd5j=\xcd\xd6e\x95r?\
-\x8d\xaa\xe2`m\x9d\x89\xd5\xdb+\xec\xb5\xaa'\xe1\xfa\
-\xcdM:]=|\xba`\xedJL\x8a\x0a\x0bk\x17\
-\xe3\xd5\x0b\xa1\x13\x0b\x9ec\xa7\x09^\xc0\xd0\x98F\x16\
-\xdc=\xda\xae\x9c\xae&\xf7\x98\x1f8\xbe\xcf\xb7\xf6n\
-\xd7i~\xbf^#\xc0\x22}\xcc\xd6\xcdl\xb5u>\
-[c\xaa\x86E\xc6[\xafds\xb3z\xc8\xd1\x82d\
-~\x9f\xb5G\xc0z\x7f\xd1\x1e\x0cV]\xafq[B\
-\x0d\x1f\xe6Kh\x10\x979v\xa9\xec\xac\xbd\x80(\xa7\
-\xa9M\x07s\x05\x04\xe0O\x8a}\xcd\x1f\xf1^\xbe?\
-\xa3\xfb\xf94\xdb\x84\xe7\xe4\xea\xe2\xab\xab\xd5.\x5c\xbf\
-\xba\xfa?`\xd4\xf8.\xdd\xde@\x0f\xb3t\xb1\xd9\x07\
-\xb2\x5c\xb9A|\x90\xbcf\xbbZd\xc0b\x93\xac>\
-\xeb\x8d8\xf2\x8bM\x83\x1a\x8b\xad\x0b\xe2~\x99\xde\xc5\
-\xd7\x8b\xd5U\xba\xe8\xc5\xf1m\xba\x9b\xdf\xce\xbff\xd3\
-\xfa(}\xb3\x0fo\xc5\x85\xc8\xaaQ\x02\x93+\xd9r\
-\xfb\x88Y\x94w\x8fX\xd6\x90>X\xc0\x84\xa8-O\
-\xcc\xa6<_^\xef0\x83\x05\xf2WENU\xd5c\
-\xa0*\xbb\xbd+jk\xe6\x89\xa2/\xf3\xcd\xfc\x0a\x0f\
-\xc2{+\x04\xd8%\xe6\x1e\x9e\xb6Jq5\x05<\xce\
-\x09\xa5\xe2f\xb5\x5c<\x16`\xa5,\xe9\x8a\x10W~\
-\x9bm\xd3i\xbaMkyR\x96p\x90\xa8%j\xd6\
-\xd3\xd9\xe5?\x7f\xf8\xb1:\xe3?\x99\x5c\xfe{\xb5\xfe\
-\xa3\x9cB\x14!@z\xb5\xba\x07\x9a\xad.=`\x12\
-\xe3\xc9%\xca\xd5t\xfby~\x0b\x0c\x83\xa9\xb1\xff{\
-w\xbb\x00\xc9VU4\x80\x11\xdbu\xa7y\xb7\xeb,\
-O}\x1d\xcc\x16>\x9d\xdc\xce\xb1\xd1\xf8_\xdb\xf9b\
-\xf13\x0e\xe2\xddE(:\x9do\x17\xd9g7f\xfe\
-X\xaeb\x5c,\xa3\xbcI\xe0\xad\xf2\xe3\xb8D\x83\xfb\
-t]\xa3\xe7Z\xab\xeav\xe86\xa4\x0eM\xa2\xf16\
-'S\xa3\x18\xf3yf\xb1z_\xa2\xf1\xba\xa2\xaaP\
-xA\x08\xa6\x18z\x1a\xa0}\x0cxj\x98\x01\x8bY\
-\xc9a\xc0\x91\xab\x96V\x13\xcc\x13Jt\x02\xda\x87\x83\
-\xed,y\x22\x89\xf0#\xdc\xf9\x14%\xf1R\xe0\xac\x81\
-\x05k\xa4\x167,\xea\xf4fVSB\x85d\x1ff\
-\x80\xc1K\xc0\xed\xf7-\xc5\xebr\x17\xbfw\xd5\xde]\
-\x99\xcdv\xbd\xfa#\xbb\x5c\xae\x965)\x16\xf7\xf2`\
-<-\x94\x7f\x83\xa5\x90x\xad\x1b\x99\xb58\xc6$\x22\
-.\xd7\x97W\xe7rIX\xc0\x00o\xe4\x87\x03\xba\x8e\
-\xa5H\x80\xf5t\xa3\xab\x10J5\x81\xb5\x81\xa6\x1ey\
-\x8fq\xb8\x98\xf8\xd6K\x07i8(\xa8+\x85\xf1\x9f\
-\xf6\x14U\xa2\x98R*\xb0*\xeeR\xbd\xb2\xc6\xfd\xa4\
-R\xf6c2*\xc5;7\xa3s\xdc\xe9@\xce\xbd\xbe\
-\xddQ\x03w\xe7\xcc8B\x9d\xe0\xcd\xb3\x140\xb9\xc8\
-L\xd7\x93\xbeE\xe0\xec[\xa9a\x8f 1\x1cY\x0b\
-\xd3\xb8$U\x0e\xefn\xe7KD}\x83\x9c\xeaz\x97\
-\xc1\xd5\x09b\x13\xaa_\xef\x02\x84Z\xd7>\x06jQ\
-jFTj\x8c\x0b\x88Q\xd9y4\x8901\xad\x15\
-R\xc8\x08d\x017 \x18Fy\xe3(\xce\xffV\x1f\
-s8`\xf4\xaa\xa6lQ\x17\x14\xb0\xb0\x0d\xd5\xbfE\
-TT\x92\xe8kh\xba\x9bm\xban\xdf&+\xeb\xb2\
-%\xcc\x9b'TP\x8c\xab\x9f\x8fL\xd0\x1d\xa0\x84\x80\
-C\x10\x03sh0\xe3\xa4\xed\xe1\xad\x93\x04\x92%G\
-\x0b$\x1b\x10Hn\xa3\xa4\xd9\xcb\xbb\xfe]\xdf^\x89\
-\x14\x10\x0e\xe7d\xb6\x8f\xe3\xeb\xe2\xc1\xe7\xb9\xee\x084\
-\x91\xda\x12\xa6\xc0\x17\xc3\xc4\xba\x8c\x0b\xc5p\x80\xea\xb9\
-\x06\x88a%\x12s\x01\x80\xc2\xe1\x12<\x1b\xc6\xbc\x10\
-LN\xd1\xa0h\xb8flT\xe5du$Mq\x8b\
-$\xa5\x22\x8aMb@'\x19\xd0Te\xd2\xd5(.\
-\x1e\x1bEe\x0b$\xef\xaa|T67\x8d\xc2\xba\x1d\
-\x19\xd5-\xa3\x12\xd6\x07\x08\xb6\xf2\x07+\xe7R\x0d\xd4\
-\x9d\x5c\xcd6\x0d\x1e\xeff\x91m\x08\x88\x9e\xeap\x12\
-\xdb\x86xj$\xbd\xad\xe4\x99\x9fI\xf9\xb0\xbc\x04\xa5\
-?\x84\x03\x02R\xb9\x22\xaa\x1es\x04\xdc@%-\x11\
-.9\xa7\xa2`b(G\xa9\xd5s\x0d\x10S\x8e\xc9\
-9\x15\x03\xd3D\xa9\xc4p\xa2E\xc7 !\x0a\xd3\xff\
-\x0c\xd3\x1c\xb5_\xe1.\x88\xfa\xcc\x5c\xd6 \x1a\xee\xc1\
-\xd7\x88\xf1\x8e\xb9\x00S\x88\x89\x10\xd8j=\xbf\x9e/\
-\xd1S\xf8%\xa2\x86\xe2\x97\x0c\x80<u\xb9\xa8`\xba\
-\xd1?\x22\x0a~\xb8\x02\x863u\xa1\xcb\xb1\xee\xe0\xa4\
-J\xac\xe1\xc0/^\x99\x16u\xdb\xaa\xd0b\xae\x0e\x03\
-\xce\xaa\xd7\x1f0q\xd9a5p]\x06\xad\xadM\xc0\
-\xc4\x13\xcc+ex\x92\x862\xbf\xc3\xaa\xc8\x1b\xb8*\
-\xf3&X\xf5V\xaf\xa3\xbb\xde\xafQ\x03\x0d\xb2x\xc0\
-\xc9\x00A\xc2s]\xc6\xd0@q\x8d\xa4I\x84T\x1c\
-8\xa5.kL\xa6,\xb4$1(PL\xdd\x1d\x05\
-\x89\xe1/_8\x94KP9\xdc\xe4\x95\x92\xf9\xed\xe0\
-\x93\x8f`\xf8X\x8f^\xb5\xab\xe6\x18X\xca\xd7h\xb8\
-\xcd\x81\x19q\xfbX\xe8C\x9e\x88&oZ'&}\
-\x1f2E0+R\xdb\x1c(g7\x12\xa0cA\x1a\
-\x80\xec\x8c1!\xbd\xb1\xb9\x9a\x07\xae\x11\x0a\xcaGT\
-$D[N\x94WV>\xa0\xb8-\xda\x00\xb6r@\
-Z\xd7v\xe1\xbd\x11\xb0\x0c\xc7\x8f];\xa3@\xfcU\
-E^\xffnR\x96\x10\x85\x82\x1a ]\xe6\xa3\x1a \
-\xee\xb6\xf9\x1a\xdd\xe2\xbc\x12\xdc\xa6\x09lL\x82\xe9\x91\
-ARD\x0c\xb3t#,R\xa8\xa2\x04i\xba,*\
-\x1f\xb0e\xde\x02j\xdd\xd91Ex^\x8d\xa35\xe0\
-\xb1\xc0\x01s\x87\xb1\xbc(?pf\xa9\x22\xb4.*\
-\xa0q5\x0e\x9e[`\x0a\x04s_\xa7P\x8d\xed\x83\
-\xe7\x04s@a\xc30T\xa3\xd7\x95G\x03\xab\x8fT\
-b\xde2\xc9\xe8H%L\x1a\xe2\xbe\xaaa\xafi\x1c\
-\xea\xd9PM\x99\xa6\xb6\xe8\xb9\xfa\xc8\x81Z`i\xb0\
-\x070\x7fp#\xad\xd5\xef{h\x0b\x5cL\xcb+\xe2\
-\xb2`\xe6hG\x5c,A.B\xda\x92\xb0b.\xea\
-\xa2\xe2oNY\xd8@\xa9\xa8\x04\xab*\xdb\xc0^\xdf\
-X\x84\xe2\xc7\xd1\x954\xa0\x89\xb8WV\xf7^\xcc\x07\
-\xd4\x81\xa3+L\xe6m\xea\xfa\xb8\xdb$'+\x0e\xa8\
-\xc8\xe9\x0at\xa85\xd4\xd1\x15~y\x00\xd8\xd5 (\
-\xc0\xd91\xc5g\xf7\x7f\x5cF\x01\x88\xf4\x8b `p\
-\xba:\xd7O\x5c\xc3\xe6\x9f\x1d\xb4\x16\xd4\x11TQ\xea\
-\xd2-\x19\xc3\x0d\xc8,\xaf\xb0j\x84\x8b)\x9a\xf1(\
-\x07\xa5,\xaf\x97Q\x13\xbeIR\xb5\xafB\x02\x09\x1d\
-j\x93W\xe2\x11\x1a\xe0\x98\x0f{\x85\x95 \xbd\xfa>\
-$\xac\x04m\x0a\xab7\xad\xf8\xa6\x15\x036\x14&\xfa\
-\x07\xa5bI\xd0\x1b\x0e\x9bd{%\xdd!\xf1\xa4Y\
-G:I\xbeW6i\x16\x15 \xfd\x82\xc9\xf5\xda\x92\
-K\xd2*\xd1#\x97\x94\xe4\xaa\x90K\x82\xc8\xa3\xe5\x12\
-\x88\x98\x86XB\xfe\x0d\x89%0\x91K\xb1\x04*\xef\
-\x90X\x02\xe9\x16\x10K\x8ah\xd6+\x96$\xd5&\x17\
-K\x92\xcb\xa3\xc4\x12k\xf8\xbe\x87#Mvp\x1c\xf0\
-M\xd8\xbc\x09\x9b\xb0\xb0Q\x5c`\x16\xc43\x08\x9b\xa0\
-\xdb\xca)\xd8g\xbc<\xbcS|\x8ae\x82\x01 \xab\
-\xac\x0b\xab)M4\x97}f\x15lo%\xb5\x18\xb8\
-\xb4\xb4\xb0\xd8\x99c)\xea\xde\xde2\xc1\xbd\xb2\xf2!\
-\x97]\xd8\x06\x0c\x9f\x12\xb0\xae\xed\xc2{#`Yn\
-\xdf\x82\xd5\xc7\xd1\xae\xca\x0b\xbc\xbe\x8b\x09\x19\xa9\x9d\xf1\
-\x0c\xc6\x9b\xb5\x1e@\xdcnQ\xda\xea\xa5\xa9.\x0dJ\
-.\x8eiM\x8b\x95\x08\xc1\x98TuQ\xf9\x90\x1b\xea\
-`\xe0Y+\xdcX\x02_\xb1\xbb\xea\x5cP\xfa\x0d\x5c\
-I\xde\xbd\xcdE|^\xe8\x16#@\xce\x00\x87\xd4e\
-e\x8b\xdc\x5cwmh\xe4\x00yQ\xeb&\xd0\x80w\
-%a!f\xfbm+j\xa1\xa9\x11\xfbm+60\
-\x96\xf2&\xd6\xde\xc4ZX\xaca\x0c\x97\x11\xdb\xf8\x92\
-\x9ds\x1aQV\xd5\x01\x04\xf0\xc2\x88.\xc4Q\x19?\
-\x00\xebM0U\x17\x15\x7fsY\xe4\x1a\xb8\xe8\x01u\
-\x01\xbb\xb2\xb2\x0d\xec\xf5\x8dER\x15\xb6\x14\xb4\x82f\
-^Y\xdd{1\x1f!s\x1f\x8fJKM]\x1fw\
-\x9b\xe4\xe2\x88\xe5\xec\xef\x04\x12\xac\x90\xe9<v\xa0r\
-\x81\xc4\xa0GpX\x8b\x82\xfcO.\x8c\x10V)'\
-\x8c\x00\x88SW\x99\x9bm\x1e\xb4,\x85\x11\x80+[\
-\x04\x0e\xca\xe5@KC$vR\x17V\x8dry\x84\
-\xcd\x8c\x88rPkFE\xdfQ\xb3\xc1^i$\x8f\
-{yg{_\xde\x05\x83PZ\xbc\xf9uo2i\
-\x80L\xe2L\x18\xab\x1a\x99\x1bO\x95IO\x88\xd1\x83\
-\x19\x06\xfcl\x1a\xaf\xc8\xdf\x88\xf6\x8dhC\xe2\xb1e\
-\x99q\xdb{* \x18\xf5b\x22\x18\xa2\xe7\xb6?D\
-\xaf\xbd\x18=\xb1\xc08\x85\x15k\x8bx\xb6\x11\x92P\
-]\x17\x15\x7fs\x15\xeb\x1a\xf0\xa8\x00cUe\x1b\xd8\
-\xeb\x1b\x8b*\x0b\xd9\x05\xa3\xa9WV\xf7\x9e\xcf\x87\x83\
-\xfb\xe2\xf4\x9e\xa1\x5c\xeb\xba>\xee6\xc9U,\xc9c\
-\x17N\xc5j\xed\x8e\xe0c\xf7\xc5b\xa4\x95\xda\xc8\xb2\
- \xff\x93\xabX\x84\xa5y`^\xe2k\x04\x8e\xf9_\
-K{\xbf\x00\x16\x95\xb9\xaf\xdd\xb7$\xb8:Q-\x06\
-\xfe*)\xfc\xc2\xaaM\xae`\xa1\x95\xb4*r\xa0\xf8\
-u\x9bE\xd7Q\xa3\xc1\xa0\xe8\xbc\xe4\x92\x19U\x9e\xd5\
-\xad>\x02E2%-\x87\x8dH\x84\xb2Bks\xe8\
-]z}<n5\xcdP\xc6\x81\xe4\x9al6\x9bz\
-\x0a\xf5{Z]\xbf\xbcut$q\xde\xd2\xc2\xbeP\
-\x86\x5c\x05\x93p\x84\xc4A\xf0I|w\x81\xe9\xfe\xf1\
-\xdb\xc9\xc1\xe8\xd0\x98\xed_\x82GH\xf1\xd5\xa5\xa4R\
-\xe5\x85V\xa2\x9fJQ^\x19\xcd\xc0\xb1D\x97\x87q\
-;R\x89\xa0Z\x08\x8d\xf6\x9b\x00~\x80Q,\xb8b\
-\xf0\x181\x09\xe4\x80\xa77\xa0\x04\x89\x13\xfc4\xfc\xca\
-^\x18\x0a\xdf\xff\xc38`\x12\xe2\xa1eK\x0cb\x99\
-&\x86q\xad8\x96)F\x8c\xc6;\xd2\xb0\x19\xe0\xfe\
-q\xe0e\x0d\xcf\xb0\xe30]&\xf0\xdb\x09\xd0\xa8S\
-`zJ|\xbd\x0f\xf2\x89\x80\x01\xa7\x82k\xf5^\xa1\
-\xf7\x9b;\xf8\xc5\x0a\x83<-\xef\x9c\xf8r\x99M\xb6\
-\xabu<\xb9_\x7fI\xb7\xf7\xeb\x0cO\xb3T{y\
-\xf08D\x11\x08\x90\xee\x90\x83\xb5\x96\xa0\xbb\xec\x0e\xe8\
-hMX\xeb\xc0\x83\xbb1N9\x80TI\xdd\xbdC\
-<\xde)\x9eC\xc7xd\xe0\x1cO\xe8 O\x05\x1f\
-U\x1d\x97\x95-X\xaf\xe3\xa2\xa6\xea\xb69\x85=\xe7\
-\x19Zg\x96\xfaN;5N2t\xb3\xdb7N2\
-tS\xe2\xd7\x02\x97\x926]xN9\x13@\x96\xb4\
-\xe7\x85\x07\xa7\xc3\x0e\x84\xf5\x1dp\x082x\xf7x\xda\
-)\xb3c\xc3B\x0656\x18\x19\x8a\xc1\xa1\xf8\xef\xdb\
-\xba\xc0\x96\xbfB\x0a\x0f\x9e\xbf\xc5oQ\x91\xa0\x86\x8d\
-\xc5\x10<\x88\x7f\xa6\xaa\x1c\xcf\x85\xeco\x9c\xe7\xe5\xb6\
-\xfa\xfe\xe3\x0e!\x80'JE\xfdm\xa4\x8d\x93k\xb8\
-98#\xcf\xdf\xdb\x15W8\xbc4\x9d\xe5y\xb2\xfc\
-\xa4\x19ltUS\x1cA\xe3x/\x13\x0c\xc4\xd6\x97\
-\xf7\xc2@\x9c\x8a\xce\x81\xa0zj\x86\xba_N\xca\x9f\
-\x9cX\xbf\x9b\x11\xf7\xcd\x1e=\xb4Y\x7f\xf3\x8b\xb7\x1a\
-\x0f\x95 y\x17x\xabg\xe4\xddo\xecbF1#\
-\x019\xc7\x0f\xdfY\xa6\xf5\x0e\xef4\xce\xe5Y\x0f)\
-%\x1e\x8b\x83\xb3^\xcd\x0e\x8f\xdf\xb5\xb0\xfe\xe8\xdf\xbb\
-\x09\xae\xf71x3\x07\xba\xd2x\x97\xc9\xf8\xe9\xee\xf7\
-\x0f\xbdw\xb6\xde\xdaz\xb6\xb0\xc0!+QH\x8f\xc6\
-\xe1\x9e]k\x12{\xbd\xec\xce\x0c\x94\x90\xd4\x82kV\
-\x0c\xccg23\xd3\xe6\xc0\x04\xac\x02\x90^J\x05&\
-\xe0Qj)\xdd\xf6Puu\xa1\x05u0`\xb7\xc4\
-y\x87c\x02;\xe7- \xb0k\x9d\x1e\xca\xa1\x9c\xb4\
-\xa05\xc7\x95'\xaf]\xb6\xa2\xee\x02\xc4>\x19\xd0\xc4\
-Q\xc0\xaa\xc1\x0b\x8a\xbd\xca\xa0\xb3\x8c=cX\xd37\
-\x06\x1d\xa0pj\xba\xab\x96X.[$\x86\x83\x91\xc9\
-[Xj\xcb\xa5\xe37\xa4\x09\xee\xb8\xb1!\x16\xf7\x8d\
-TL\xccQ\x84\x86\xff\xbaK\xd0O\xd8\x92A\xd6C\
-\xc0\xa7\x18!\x07\xe5\xfe\xc4\xfb\xeer\x0f5x\xc2|\
-\xe5Q\xdb+\x0e`\xb1\xc1\x04l\xef\xdex\xdb\x17\xda\
-\xda\xb84\x17\xcc\xc1\xddm\x0ctpk\xabkS'\
-\xa1j\x88\xe9\xb5g\xa7\xe2\xa3\xf76>\xc3\xe6\x12{\
-\xdc\xe6\xdaa\x9b\x1b\xe0\xdd\xee\xfez{\x18\xdc\xe2\xfc\
-&js\x8b\xdb2v\x9f\xf4\xf6E*\xab\xf5_\xb9\
-\x12A*\xb5\xb0O\xe2\xb1\x135\xcfy\xb6w \xe7\
-J\x22\xf6o.\x19\xf4\xf2\xd0C\x89\x0a o\x80T\
-vN9\xf3\xa4\xb2D\x7f\xd6\x04\x18\xf7\x04\x0a%f\
-\xc8}\x8az\x11\x92\x9c&~\xca9W\x8b(V\xb5\
-G\xb5\xd4\xb5{z8Q\xb7\xc0\x02\xd8\xe9\xecL\xb4\
->N\xdd?M\xb2\x94V\xc1\xc9\xd6\x19\x90\xa8\xe8g\
-\xd8\x1e\xeb,\xc6o\x8cT\xc6\x17(\xb5\xc4\xe8\x11(\
-\xc2\xef\xa4I\xbb\x076-h\xa0\xc1\x1a\xd4Sx\xf4\
-Tk\xa0W\xa4<\xb6\xc9q\xd7v\x09OZ*%\
-\xfdR\xb3w\xa9\x94\x0c\x89\xb0\x1d\x10\x86\x03\x85\xe70\
-\x02>0\xdd!w\x17=\xd4\x84T\xcf\x10\x9b\xb6\xa3\
-\x16k2=\x8by\xce\x8f1\xcf\xc1\x9a\xd7\x03\xd6\xd1\
-\xf6`v\x01\x8b,\xe0\x11\xf5;\x85\x87\x9c\xa9\xce\x08\
-\x87h\xb8\xe5Xxk\xb4O\xa0\x0b\xce\x8f#\xe3\xde\
-\x95v&\xa0\x8d\x0b^\x1c\xebp\xd7k\xab\xbe\xf1\xcc\
-\xd74\x9e\xe4l \xcd\x13\x0f\x1d\x05X\x8aW\x9c\xf3\
-G\xbc\x1f\xff\xf9\xdd\xff\x03\x0b\xb0\x0a\x0c\x99\x9b\x00\x00\
-\
-"
-
-qt_resource_name = b"\
-\x00\x05\
-\x00o\xa6S\
-\x00i\
-\x00c\x00o\x00n\x00s\
-\x00\x06\
-\x07\x03}\xc3\
-\x00i\
-\x00m\x00a\x00g\x00e\x00s\
-\x00\x16\
-\x02\x1b\xe1\x0a\
-\x00g\
-\x00o\x00-\x00n\x00e\x00x\x00t\x00-\x00v\x00i\x00e\x00w\x00-\x00p\x00a\x00g\x00e\
-\x00.\x00s\x00v\x00g\x00z\
-\x00\x11\
-\x04\xf3\xa4*\
-\x00g\
-\x00o\x00-\x00n\x00e\x00x\x00t\x00-\x00v\x00i\x00e\x00w\x00.\x00s\x00v\x00g\x00z\
-\
-\x00\x15\
-\x01\x09v*\
-\x00g\
-\x00o\x00-\x00p\x00r\x00e\x00v\x00i\x00o\x00u\x00s\x00-\x00v\x00i\x00e\x00w\x00.\
-\x00s\x00v\x00g\x00z\
-\x00\x0d\
-\x0e\xb9\xa6*\
-\x00z\
-\x00o\x00o\x00m\x00-\x00o\x00u\x00t\x00.\x00s\x00v\x00g\x00z\
-\x00\x12\
-\x0al\x90\xca\
-\x00d\
-\x00o\x00c\x00u\x00m\x00e\x00n\x00t\x00-\x00o\x00p\x00e\x00n\x00.\x00s\x00v\x00g\
-\x00z\
-\x00\x1a\
-\x01d\xbbJ\
-\x00g\
-\x00o\x00-\x00p\x00r\x00e\x00v\x00i\x00o\x00u\x00s\x00-\x00v\x00i\x00e\x00w\x00-\
-\x00p\x00a\x00g\x00e\x00.\x00s\x00v\x00g\x00z\
-\x00\x0c\
-\x009l\x8a\
-\x00z\
-\x00o\x00o\x00m\x00-\x00i\x00n\x00.\x00s\x00v\x00g\x00z\
-"
-
-qt_resource_struct = b"\
-\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
-\x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
-\x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x00\x10\x00\x02\x00\x00\x00\x07\x00\x00\x00\x03\
-\x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x010\x00\x00\x00\x00\x00\x01\x00\x00\xba\xe2\
-\x00\x00\x01\x81\x8a\xd9\xf0\x94\
-\x00\x00\x00|\x00\x00\x00\x00\x00\x01\x00\x00J'\
-\x00\x00\x01\x81\x8a\xd9\xf0\x94\
-\x00\x00\x00\xf6\x00\x00\x00\x00\x00\x01\x00\x00\x89\xa4\
-\x00\x00\x01\x81\x8a\xd9\xf0\x94\
-\x00\x00\x00\x22\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
-\x00\x00\x01\x81\x8a\xd9\xf0\x94\
-\x00\x00\x00T\x00\x00\x00\x00\x00\x01\x00\x001K\
-\x00\x00\x01\x81\x8a\xd9\xf0\x94\
-\x00\x00\x00\xcc\x00\x00\x00\x00\x00\x01\x00\x00x\xec\
-\x00\x00\x01\x81\x8a\xd9\xf0\x94\
-\x00\x00\x00\xac\x00\x00\x00\x00\x00\x01\x00\x00c\xbc\
-\x00\x00\x01\x81\x8a\xd9\xf0\x94\
-"
-
-def qInitResources():
- QtCore.qRegisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
-
-def qCleanupResources():
- QtCore.qUnregisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
-
-qInitResources()
################################################################################
## Form generated from reading UI file 'mainwindow.ui'
##
-## Created by: Qt User Interface Compiler version 6.4.0
+## Created by: Qt User Interface Compiler version 6.7.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
QMenuBar, QSizePolicy, QSplitter, QStatusBar,
QTabWidget, QToolBar, QTreeView, QVBoxLayout,
QWidget)
-import resources_rc
+import rc_resources
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
self.splitter.setOrientation(Qt.Horizontal)
self.tabWidget = QTabWidget(self.splitter)
self.tabWidget.setObjectName(u"tabWidget")
- sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
+ sizePolicy = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.tabWidget.sizePolicy().hasHeightForWidth())
self.splitter.addWidget(self.tabWidget)
self.pdfView = QPdfView(self.splitter)
self.pdfView.setObjectName(u"pdfView")
- sizePolicy1 = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
sizePolicy1.setHorizontalStretch(10)
sizePolicy1.setVerticalStretch(0)
sizePolicy1.setHeightForWidth(self.pdfView.sizePolicy().hasHeightForWidth())
self.mainToolBar.setObjectName(u"mainToolBar")
self.mainToolBar.setMovable(False)
self.mainToolBar.setFloatable(False)
- MainWindow.addToolBar(Qt.TopToolBarArea, self.mainToolBar)
+ MainWindow.addToolBar(Qt.ToolBarArea.TopToolBarArea, self.mainToolBar)
self.statusBar = QStatusBar(MainWindow)
self.statusBar.setObjectName(u"statusBar")
MainWindow.setStatusBar(self.statusBar)
list member storing the Person instances.
In QML, the type of a list properties - and the guests property is a list of
-people - are all of type ListProperty. ListProperty is simple value
-type that contains a set of functions. QML calls these functions
-whenever it needs to read from, write to or otherwise interact with
-the list. In addition to concrete lists like the people list used in this
-example, the use of QQmlListProperty allows for "virtual lists" and other advanced
+people - are all of type :class:`~PySide6.QtQml.ListProperty`.
+``ListProperty`` is a simple value type that contains a set of functions.
+QML calls these functions whenever it needs to read from, write to or otherwise
+interact with the list. In addition to concrete lists like the people list used in this
+example, the use of ``ListProperty`` allows for "virtual lists" and other advanced
scenarios.
Running the Example
:lines: 4-32
To do this, we replace the ``pieSlice`` property in ``PieChart`` with a
-``slices`` property, declared as a class variable of the ``QQmlListProperty``
-type. The ``QQmlListProperty`` class enables the creation of list properties in
+``slices`` property, declared as a class variable of the
+:class:`~PySide6.QtQml.ListProperty` type.
+The ``ListProperty`` class enables the creation of list properties in
QML extensions. We replace the ``pieSlice()`` function with a ``slices()``
function that returns a list of slices, and add an internal ``appendSlice()``
function (discussed below). We also use a list to store the internal list of
:lines: 75-79
Although the ``slices`` property does not have an associated setter, it is
-still modifiable because of the way ``QQmlListProperty`` works. We indicate
+still modifiable because of the way ``ListProperty`` works. We indicate
that the internal ``PieChart.appendSlice()`` function is to be called whenever
a request is made from QML to add items to the list.
The ``appendSlice()`` function simply sets the parent item as before, and adds
the new item to the ``_slices`` list. As you can see, the append function for
-a ``QQmlListProperty`` is called with two arguments: the list property, and the
+a ``ListProperty`` is called with two arguments: the list property, and the
item that is to be appended.
The ``PieSlice`` class has also been modified to include ``fromAngle`` and
if not engine.rootObjects():
sys.exit(-1)
+ ex = app.exec()
del engine
- sys.exit(app.exec())
+ sys.exit(ex)
import QtQuick.Layouts
import FileSystemModule
+pragma ComponentBehavior: Bound
+
ApplicationWindow {
id: root
+
+ property bool expandPath: false
+ property bool showLineNumbers: true
+ property string currentFilePath: ""
+
width: 1100
height: 600
+ minimumWidth: 200
+ minimumHeight: 100
visible: true
+ color: Colors.background
flags: Qt.Window | Qt.FramelessWindowHint
- title: qsTr("Qt Quick Controls - File System Explorer")
+ title: qsTr("File System Explorer Example")
- property string currentFilePath: ""
- property bool expandPath: false
+ function getInfoText() : string {
+ let out = root.currentFilePath
+ if (!out)
+ return qsTr("File System Explorer")
+ return root.expandPath ? out : out.substring(out.lastIndexOf("/") + 1, out.length)
+ }
menuBar: MyMenuBar {
- rootWindow: root
-
- infoText: currentFilePath
- ? (expandPath ? currentFilePath
- : currentFilePath.substring(currentFilePath.lastIndexOf("/") + 1, currentFilePath.length))
- : "File System Explorer"
-
+ dragWindow: root
+ infoText: root.getInfoText()
MyMenu {
title: qsTr("File")
Action {
text: qsTr("Increase Font")
shortcut: StandardKey.ZoomIn
- onTriggered: textArea.font.pixelSize += 1
+ onTriggered: editor.text.font.pixelSize += 1
}
Action {
text: qsTr("Decrease Font")
shortcut: StandardKey.ZoomOut
- onTriggered: textArea.font.pixelSize -= 1
+ onTriggered: editor.text.font.pixelSize -= 1
}
Action {
- text: expandPath ? qsTr("Toggle Short Path") : qsTr("Toggle Expand Path")
- enabled: currentFilePath
- onTriggered: expandPath = !expandPath
+ text: root.showLineNumbers ? qsTr("Toggle Line Numbers OFF")
+ : qsTr("Toggle Line Numbers ON")
+ shortcut: "Ctrl+L"
+ onTriggered: root.showLineNumbers = !root.showLineNumbers
+ }
+ Action {
+ text: root.expandPath ? qsTr("Toggle Short Path")
+ : qsTr("Toggle Expand Path")
+ enabled: root.currentFilePath
+ onTriggered: root.expandPath = !root.expandPath
+ }
+ Action {
+ text: qsTr("Reset Filesystem")
+ enabled: sidebar.currentTabIndex === 1
+ onTriggered: fileSystemView.rootIndex = undefined
}
Action {
text: qsTr("Exit")
onTriggered: Qt.exit(0)
- shortcut: "Ctrl+Q"
+ shortcut: StandardKey.Quit
}
}
Action {
text: qsTr("Cut")
shortcut: StandardKey.Cut
- enabled: textArea.selectedText.length > 0
- onTriggered: textArea.cut()
+ enabled: editor.text.selectedText.length > 0
+ onTriggered: editor.text.cut()
}
Action {
text: qsTr("Copy")
shortcut: StandardKey.Copy
- enabled: textArea.selectedText.length > 0
- onTriggered: textArea.copy()
+ enabled: editor.text.selectedText.length > 0
+ onTriggered: editor.text.copy()
}
Action {
text: qsTr("Paste")
shortcut: StandardKey.Paste
- enabled: textArea.canPaste
- onTriggered: textArea.paste()
+ enabled: editor.text.canPaste
+ onTriggered: editor.text.paste()
}
Action {
text: qsTr("Select All")
shortcut: StandardKey.SelectAll
- enabled: textArea.length > 0
- onTriggered: textArea.selectAll()
+ enabled: editor.text.length > 0
+ onTriggered: editor.text.selectAll()
}
Action {
text: qsTr("Undo")
shortcut: StandardKey.Undo
- enabled: textArea.canUndo
- onTriggered: textArea.undo()
+ enabled: editor.text.canUndo
+ onTriggered: editor.text.undo()
}
}
}
-
- Rectangle {
+ // Set up the layout of the main components in a row:
+ // [ Sidebar, Navigation, Editor ]
+ RowLayout {
anchors.fill: parent
- color: Colors.background
-
- RowLayout {
- anchors.fill: parent
- spacing: 0
-
- // Stores the buttons that navigate the application.
- Sidebar {
- id: sidebar
- rootWindow: root
-
- Layout.preferredWidth: 60
- Layout.fillHeight: true
- }
+ spacing: 0
+
+ // Stores the buttons that navigate the application.
+ Sidebar {
+ id: sidebar
+ dragWindow: root
+ Layout.preferredWidth: 50
+ Layout.fillHeight: true
+ }
- // Allows resizing parts of the UI.
- SplitView {
- Layout.fillWidth: true
- Layout.fillHeight: true
-
- handle: Rectangle {
- implicitWidth: 10
- color: SplitHandle.pressed ? Colors.color2 : Colors.background
- border.color: Colors.color2
- opacity: SplitHandle.hovered || SplitHandle.pressed ? 1.0 : 0.0
-
- Behavior on opacity {
- OpacityAnimator {
- duration: 900
- }
+ // Allows resizing parts of the UI.
+ SplitView {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ // Customized handle to drag between the Navigation and the Editor.
+ handle: Rectangle {
+ implicitWidth: 10
+ color: SplitHandle.pressed ? Colors.color2 : Colors.background
+ border.color: SplitHandle.hovered ? Colors.color2 : Colors.background
+ opacity: SplitHandle.hovered || navigationView.width < 15 ? 1.0 : 0.0
+
+ Behavior on opacity {
+ OpacityAnimator {
+ duration: 1400
}
}
+ }
- // We use an inline component to make a reusable TextArea component.
- // This is convenient when the component is only used in one file.
- component MyTextArea: TextArea {
- antialiasing: true
- color: Colors.textFile
- selectedTextColor: Colors.textFile
- selectionColor: Colors.selection
- renderType: Text.QtRendering
- textFormat: TextEdit.PlainText
-
- background: null
- }
-
- Rectangle {
- color: Colors.surface1
-
- SplitView.preferredWidth: 250
- SplitView.fillHeight: true
-
- StackLayout {
- currentIndex: sidebar.currentTabIndex
-
- anchors.fill: parent
-
- // Shows the help text.
- MyTextArea {
- readOnly: true
- text: qsTr("This example shows how to use and visualize the file system.\n\n"
- + "Customized Qt Quick Components have been used to achieve this look.\n\n"
- + "You can edit the files but they won't be changed on the file system.\n\n"
- + "Click on the folder icon to the left to get started.")
- wrapMode: TextArea.Wrap
- }
-
- // Shows the files on the file system.
- FileSystemView {
- id: fileSystemView
- color: Colors.surface1
-
- onFileClicked: (path) => root.currentFilePath = path
- }
+ Rectangle {
+ id: navigationView
+ color: Colors.surface1
+ SplitView.preferredWidth: 250
+ SplitView.fillHeight: true
+ // The stack-layout provides different views, based on the
+ // selected buttons inside the sidebar.
+ StackLayout {
+ anchors.fill: parent
+ currentIndex: sidebar.currentTabIndex
+
+ // Shows the help text.
+ Text {
+ text: qsTr("This example shows how to use and visualize the file system.\n\n"
+ + "Customized Qt Quick Components have been used to achieve this look.\n\n"
+ + "You can edit the files but they won't be changed on the file system.\n\n"
+ + "Click on the folder icon to the left to get started.")
+ wrapMode: TextArea.Wrap
+ color: Colors.text
}
- }
-
- // The ScrollView that contains the TextArea which shows the file's content.
- ScrollView {
- leftPadding: 20
- topPadding: 20
- bottomPadding: 20
- clip: true
-
- SplitView.fillWidth: true
- SplitView.fillHeight: true
-
- property alias textArea: textArea
- MyTextArea {
- id: textArea
- text: FileSystemModel.readFile(root.currentFilePath)
+ // Shows the files on the file system.
+ FileSystemView {
+ id: fileSystemView
+ color: Colors.surface1
+ onFileClicked: path => root.currentFilePath = path
}
}
}
+
+ // The main view that contains the editor.
+ Editor {
+ id: editor
+ showLineNumbers: root.showLineNumbers
+ currentFilePath: root.currentFilePath
+ SplitView.fillWidth: true
+ SplitView.fillHeight: true
+ }
}
- ResizeButton {}
+ }
+
+ ResizeButton {
+ resizeWindow: root
}
}
<file>qmldir</file>
<file>Main.qml</file>
<file>qml/About.qml</file>
+ <file>qml/Editor.qml</file>
<file>qml/Colors.qml</file>
<file>qml/FileSystemView.qml</file>
- <file>qml/Icon.qml</file>
<file>qml/MyMenu.qml</file>
<file>qml/MyMenuBar.qml</file>
<file>qml/ResizeButton.qml</file>
<RCC>
<qresource>
+ <file>icons/app_icon.svg</file>
<file>icons/folder_closed.svg</file>
<file>icons/folder_open.svg</file>
<file>icons/generic_file.svg</file>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill="#EBDBB2" d="M13.25 8.5a.75.75 0 1 1-.75-.75.75.75 0 0 1 .75.75zM9.911 21.35l.816.578C10.819 21.798 13 18.666 13 13h-1a15.503 15.503 0 0 1-2.089 8.35zM4 6.703V10a2.002 2.002 0 0 1-2 2v1a2.002 2.002 0 0 1 2 2v3.297A3.707 3.707 0 0 0 7.703 22H9v-1H7.703A2.706 2.706 0 0 1 5 18.297V15a2.999 2.999 0 0 0-1.344-2.5A2.999 2.999 0 0 0 5 10V6.703A2.706 2.706 0 0 1 7.703 4H9V3H7.703A3.707 3.707 0 0 0 4 6.703zM20 10V6.703A3.707 3.707 0 0 0 16.297 3H15v1h1.297A2.706 2.706 0 0 1 19 6.703V10a2.999 2.999 0 0 0 1.344 2.5A2.999 2.999 0 0 0 19 15v3.297A2.706 2.706 0 0 1 16.297 21H15v1h1.297A3.707 3.707 0 0 0 20 18.297V15a2.002 2.002 0 0 1 2-2v-1a2.002 2.002 0 0 1-2-2z"/><path fill="none" d="M0 0h24v24H0z"/></svg>
ApplicationWindow {
id: root
- width: 500
- height: 360
+ width: 650
+ height: 550
flags: Qt.Window | Qt.FramelessWindowHint
color: Colors.surface1
menuBar: MyMenuBar {
id: menuBar
- implicitHeight: 20
- rootWindow: root
+
+ dragWindow: root
+ implicitHeight: 27
infoText: "About Qt"
}
Image {
id: logo
+
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: 20
+
source: "../icons/qt_logo.svg"
- sourceSize: Qt.size(80, 80)
+ sourceSize.width: 80
+ sourceSize.height: 80
fillMode: Image.PreserveAspectFit
+
smooth: true
antialiasing: true
asynchronous: true
}
- TextArea {
- anchors.top: logo.bottom
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.bottom: parent.bottom
- anchors.margins: 20
- antialiasing: true
- wrapMode: Text.WrapAnywhere
- color: Colors.textFile
- horizontalAlignment: Text.AlignHCenter
- readOnly: true
- selectionColor: Colors.selection
- text: qsTr("Qt Group (Nasdaq Helsinki: QTCOM) is a global software company with a strong \
-presence in more than 70 industries and is the leading independent technology behind 1+ billion \
-devices and applications. Qt is used by major global companies and developers worldwide, and the \
-technology enables its customers to deliver exceptional user experiences and advance their digital \
-transformation initiatives. Qt achieves this through its cross-platform software framework for the \
-development of apps and devices, under both commercial and open-source licenses.")
- background: Rectangle {
- color: "transparent"
- }
+ ScrollView {
+ anchors.top: logo.bottom
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ anchors.margins: 20
+
+ TextArea {
+ selectedTextColor: Colors.textFile
+ selectionColor: Colors.selection
+ horizontalAlignment: Text.AlignHCenter
+ textFormat: Text.RichText
+
+ text: qsTr("<h3>About Qt</h3>"
+ + "<p>This program uses Qt version %1.</p>"
+ + "<p>Qt is a C++ toolkit for cross-platform application "
+ + "development.</p>"
+ + "<p>Qt provides single-source portability across all major desktop "
+ + "operating systems. It is also available for embedded Linux and other "
+ + "embedded and mobile operating systems.</p>"
+ + "<p>Qt is available under multiple licensing options designed "
+ + "to accommodate the needs of our various users.</p>"
+ + "<p>Qt licensed under our commercial license agreement is appropriate "
+ + "for development of proprietary/commercial software where you do not "
+ + "want to share any source code with third parties or otherwise cannot "
+ + "comply with the terms of GNU (L)GPL.</p>"
+ + "<p>Qt licensed under GNU (L)GPL is appropriate for the "
+ + "development of Qt applications provided you can comply with the terms "
+ + "and conditions of the respective licenses.</p>"
+ + "<p>Please see <a href=\"http://%2/\">%2</a> "
+ + "for an overview of Qt licensing.</p>"
+ + "<p>Copyright (C) %3 The Qt Company Ltd and other "
+ + "contributors.</p>"
+ + "<p>Qt and the Qt logo are trademarks of The Qt Company Ltd.</p>"
+ + "<p>Qt is The Qt Company Ltd product developed as an open source "
+ + "project. See <a href=\"http://%4/\">%4</a> for more information.</p>")
+ .arg(Application.version).arg("qt.io/licensing").arg("2023").arg("qt.io")
+ color: Colors.textFile
+ wrapMode: Text.WordWrap
+ readOnly: true
+ antialiasing: true
+ background: null
+
+ onLinkActivated: function(link) {
+ Qt.openUrlExternally(link)
+ }
+ }
+ }
+
+ ResizeButton {
+ resizeWindow: root
}
- ResizeButton {}
}
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-pragma Singleton
import QtQuick
+pragma Singleton
+
QtObject {
- readonly property color background: "#23272E"
- readonly property color surface1: "#1E2227"
+ readonly property color background: "#292828"
+ readonly property color surface1: "#171819"
readonly property color surface2: "#090A0C"
- readonly property color text: "#ABB2BF"
- readonly property color textFile: "#C5CAD3"
- readonly property color disabledText: "#454D5F"
- readonly property color selection: "#2C313A"
- readonly property color active: "#23272E"
- readonly property color inactive: "#3E4452"
- readonly property color folder: "#3D4451"
- readonly property color icon: "#3D4451"
- readonly property color iconIndicator: "#E5C07B"
- readonly property color color1: "#E06B74"
- readonly property color color2: "#62AEEF"
+ readonly property color text: "#D4BE98"
+ readonly property color textFile: "#E1D2B7"
+ readonly property color disabledText: "#2C313A"
+ readonly property color selection: "#4B4A4A"
+ readonly property color active: "#292828"
+ readonly property color inactive: "#383737"
+ readonly property color folder: "#383737"
+ readonly property color icon: "#383737"
+ readonly property color iconIndicator: "#D5B35D"
+ readonly property color color1: "#A7B464"
+ readonly property color color2: "#D3869B"
}
--- /dev/null
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+import FileSystemModule
+
+pragma ComponentBehavior: Bound
+
+// This is the text editor that displays the currently open file, including
+// their corresponding line numbers.
+Rectangle {
+ id: root
+
+ required property string currentFilePath
+ required property bool showLineNumbers
+ property alias text: textArea
+ property int currentLineNumber: -1
+ property int rowHeight: Math.ceil(fontMetrics.lineSpacing)
+
+ color: Colors.background
+
+ onWidthChanged: textArea.update()
+ onHeightChanged: textArea.update()
+
+ RowLayout {
+ anchors.fill: parent
+ // We use a flickable to synchronize the position of the editor and
+ // the line numbers. This is necessary because the line numbers can
+ // extend the available height.
+ Flickable {
+ id: lineNumbers
+
+ // Calculate the width based on the logarithmic scale.
+ Layout.preferredWidth: fontMetrics.averageCharacterWidth
+ * (Math.floor(Math.log10(textArea.lineCount)) + 1) + 10
+ Layout.fillHeight: true
+
+ interactive: false
+ contentY: editorFlickable.contentY
+ visible: textArea.text !== "" && root.showLineNumbers
+
+ Column {
+ anchors.fill: parent
+ Repeater {
+ id: repeatedLineNumbers
+
+ model: LineNumberModel {
+ lineCount: textArea.text !== "" ? textArea.lineCount : 0
+ }
+
+ delegate: Item {
+ required property int index
+
+ width: parent.width
+ height: root.rowHeight
+ Label {
+ id: numbers
+
+ text: parent.index + 1
+
+ width: parent.width
+ height: parent.height
+ horizontalAlignment: Text.AlignLeft
+ verticalAlignment: Text.AlignVCenter
+
+ color: (root.currentLineNumber === parent.index)
+ ? Colors.iconIndicator : Qt.darker(Colors.text, 2)
+ font: textArea.font
+ }
+ Rectangle {
+ id: indicator
+
+ anchors.left: numbers.right
+ width: 1
+ height: parent.height
+ color: Qt.darker(Colors.text, 3)
+ }
+ }
+ }
+ }
+ }
+
+ Flickable {
+ id: editorFlickable
+
+ property alias textArea: textArea
+
+ // We use an inline component to customize the horizontal and vertical
+ // scroll-bars. This is convenient when the component is only used in one file.
+ component MyScrollBar: ScrollBar {
+ id: scrollBar
+ background: Rectangle {
+ implicitWidth: scrollBar.interactive ? 8 : 4
+ implicitHeight: scrollBar.interactive ? 8 : 4
+
+ opacity: scrollBar.active && scrollBar.size < 1.0 ? 1.0 : 0.0
+ color: Colors.background
+ Behavior on opacity {
+ OpacityAnimator {
+ duration: 500
+ }
+ }
+ }
+ contentItem: Rectangle {
+ implicitWidth: scrollBar.interactive ? 8 : 4
+ implicitHeight: scrollBar.interactive ? 8 : 4
+ opacity: scrollBar.active && scrollBar.size < 1.0 ? 1.0 : 0.0
+ color: Colors.color1
+ Behavior on opacity {
+ OpacityAnimator {
+ duration: 1000
+ }
+ }
+ }
+ }
+
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ ScrollBar.horizontal: MyScrollBar {}
+ ScrollBar.vertical: MyScrollBar {}
+
+ boundsBehavior: Flickable.StopAtBounds
+
+ TextArea.flickable: TextArea {
+ id: textArea
+ anchors.fill: parent
+
+ focus: false
+ topPadding: 0
+ leftPadding: 10
+
+ text: FileSystemModel.readFile(root.currentFilePath)
+ tabStopDistance: fontMetrics.averageCharacterWidth * 4
+
+ // Grab the current line number from the C++ interface.
+ onCursorPositionChanged: {
+ root.currentLineNumber = FileSystemModel.currentLineNumber(
+ textArea.textDocument, textArea.cursorPosition)
+ }
+
+ color: Colors.textFile
+ selectedTextColor: Colors.textFile
+ selectionColor: Colors.selection
+
+ textFormat: TextEdit.PlainText
+ renderType: Text.QtRendering
+ selectByMouse: true
+ antialiasing: true
+ background: null
+ }
+
+ FontMetrics {
+ id: fontMetrics
+ font: textArea.font
+ }
+ }
+ }
+}
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
-import QtQuick.Layouts
+import QtQuick.Effects
import QtQuick.Controls.Basic
import FileSystemModule
+pragma ComponentBehavior: Bound
+
// This is the file system view which gets populated by the C++ model.
Rectangle {
id: root
signal fileClicked(string filePath)
+ property alias rootIndex: fileSystemTreeView.rootIndex
TreeView {
id: fileSystemTreeView
+
+ property int lastIndex: -1
+
anchors.fill: parent
model: FileSystemModel
+ rootIndex: FileSystemModel.rootIndex
boundsBehavior: Flickable.StopAtBounds
boundsMovement: Flickable.StopAtBounds
clip: true
- property int lastIndex: -1
-
Component.onCompleted: fileSystemTreeView.toggleExpanded(0)
// The delegate represents a single entry in the filesystem.
implicitWidth: fileSystemTreeView.width > 0 ? fileSystemTreeView.width : 250
implicitHeight: 25
+ // Since we have the 'ComponentBehavior Bound' pragma, we need to
+ // require these properties from our model. This is a convenient way
+ // to bind the properties provided by the model's role names.
required property int index
required property url filePath
+ required property string fileName
- indicator: null
-
- contentItem: Item {
- anchors.fill: parent
+ indicator: Image {
+ id: directoryIcon
- Icon {
- id: directoryIcon
- x: leftMargin + (depth * indentation)
- anchors.verticalCenter: parent.verticalCenter
- path: treeDelegate.hasChildren
- ? (treeDelegate.expanded ? "../icons/folder_open.svg" : "../icons/folder_closed.svg")
+ x: treeDelegate.leftMargin + (treeDelegate.depth * treeDelegate.indentation)
+ anchors.verticalCenter: parent.verticalCenter
+ source: treeDelegate.hasChildren ? (treeDelegate.expanded
+ ? "../icons/folder_open.svg" : "../icons/folder_closed.svg")
: "../icons/generic_file.svg"
- iconColor: (treeDelegate.expanded && treeDelegate.hasChildren) ? Colors.color2 : Colors.folder
- }
- Text {
- anchors.left: directoryIcon.right
- anchors.verticalCenter: parent.verticalCenter
- width: parent.width
- text: model.fileName
- color: Colors.text
- }
+ sourceSize.width: 20
+ sourceSize.height: 20
+ fillMode: Image.PreserveAspectFit
+
+ smooth: true
+ antialiasing: true
+ asynchronous: true
+ }
+
+ contentItem: Text {
+ text: treeDelegate.fileName
+ color: Colors.text
}
background: Rectangle {
- color: treeDelegate.index === fileSystemTreeView.lastIndex
+ color: (treeDelegate.index === fileSystemTreeView.lastIndex)
? Colors.selection
: (hoverHandler.hovered ? Colors.active : "transparent")
}
- TapHandler {
- onSingleTapped: {
- fileSystemTreeView.toggleExpanded(row)
- fileSystemTreeView.lastIndex = index
- // If this model item doesn't have children, it means it's representing a file.
- if (!treeDelegate.hasChildren)
- root.fileClicked(filePath)
+ // We color the directory icons with this MultiEffect, where we overlay
+ // the colorization color ontop of the SVG icons.
+ MultiEffect {
+ id: iconOverlay
+
+ anchors.fill: directoryIcon
+ source: directoryIcon
+ colorization: 1.0
+ brightness: 1.0
+ colorizationColor: {
+ const isFile = treeDelegate.index === fileSystemTreeView.lastIndex
+ && !treeDelegate.hasChildren;
+ if (isFile)
+ return Qt.lighter(Colors.folder, 3)
+
+ const isExpandedFolder = treeDelegate.expanded && treeDelegate.hasChildren;
+ if (isExpandedFolder)
+ return Colors.color2
+ else
+ return Colors.folder
}
}
+
HoverHandler {
id: hoverHandler
}
+
+ TapHandler {
+ acceptedButtons: Qt.LeftButton | Qt.RightButton
+ onSingleTapped: (eventPoint, button) => {
+ switch (button) {
+ case Qt.LeftButton:
+ fileSystemTreeView.toggleExpanded(treeDelegate.row)
+ fileSystemTreeView.lastIndex = treeDelegate.index
+ // If this model item doesn't have children, it means it's
+ // representing a file.
+ if (!treeDelegate.hasChildren)
+ root.fileClicked(treeDelegate.filePath)
+ break;
+ case Qt.RightButton:
+ if (treeDelegate.hasChildren)
+ contextMenu.popup();
+ break;
+ }
+ }
+ }
+
+ MyMenu {
+ id: contextMenu
+ Action {
+ text: qsTr("Set as root index")
+ onTriggered: {
+ fileSystemTreeView.rootIndex = fileSystemTreeView.index(treeDelegate.row, 0)
+ }
+ }
+ Action {
+ text: qsTr("Reset root index")
+ onTriggered: fileSystemTreeView.rootIndex = undefined
+ }
+ }
}
// Provide our own custom ScrollIndicator for the TreeView.
contentItem: Rectangle {
implicitWidth: 6
implicitHeight: 6
+
color: Colors.color1
opacity: fileSystemTreeView.movingVertically ? 0.5 : 0.0
+++ /dev/null
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Effects
-
-// Custom Component for displaying Icons
-Item {
- id: root
-
- required property url path
- property real padding: 5
- property real size: 30
- property alias iconColor: overlay.colorizationColor
- property alias hovered: mouse.hovered
-
- width: size
- height: size
-
- Image {
- id: icon
- anchors.fill: root
- anchors.margins: padding
- source: path
- sourceSize: Qt.size(size, size)
- fillMode: Image.PreserveAspectFit
- smooth: true
- antialiasing: true
- asynchronous: true
- }
-
- MultiEffect {
- id: overlay
- anchors.fill: icon
- source: icon
- colorization: 1.0
- brightness: 1.0
- }
-
- HoverHandler {
- id: mouse
- acceptedDevices: PointerDevice.Mouse
- }
-}
Menu {
id: root
- background: Rectangle {
- implicitWidth: 200
- implicitHeight: 40
- color: Colors.surface2
- }
-
delegate: MenuItem {
id: menuItem
- implicitWidth: 200
- implicitHeight: 40
contentItem: Item {
Text {
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 5
+
text: menuItem.text
color: enabled ? Colors.text : Colors.disabledText
}
Rectangle {
+ id: indicator
+
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
width: 6
height: parent.height
+
visible: menuItem.highlighted
color: Colors.color2
}
}
background: Rectangle {
+ implicitWidth: 210
+ implicitHeight: 35
color: menuItem.highlighted ? Colors.active : "transparent"
}
}
+ background: Rectangle {
+ implicitWidth: 210
+ implicitHeight: 35
+ color: Colors.surface2
+ }
}
import QtQuick.Controls.Basic
import FileSystemModule
-// The MenuBar also serves as a controller for our Window as we don't use any decorations.
+// The MenuBar also serves as a controller for our window as we don't use any decorations.
MenuBar {
id: root
- required property ApplicationWindow rootWindow
+ required property ApplicationWindow dragWindow
property alias infoText: windowInfo.text
- implicitHeight: 25
-
- // The top level menus on the left side
+ // Customization of the top level menus inside the MenuBar
delegate: MenuBarItem {
id: menuBarItem
- implicitHeight: 25
contentItem: Text {
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
- color: menuBarItem.highlighted ? Colors.textFile : Colors.text
- opacity: enabled ? 1.0 : 0.3
+
text: menuBarItem.text
- elide: Text.ElideRight
font: menuBarItem.font
+ elide: Text.ElideRight
+ color: menuBarItem.highlighted ? Colors.textFile : Colors.text
+ opacity: enabled ? 1.0 : 0.3
}
background: Rectangle {
+ id: background
+
color: menuBarItem.highlighted ? Colors.selection : "transparent"
Rectangle {
id: indicator
+
width: 0; height: 3
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
- color: Colors.color1
+ color: Colors.color1
states: State {
- name: "active"; when: menuBarItem.highlighted
- PropertyChanges { target: indicator; width: parent.width }
+ name: "active"
+ when: menuBarItem.highlighted
+ PropertyChanges {
+ indicator.width: background.width - 2
+ }
}
-
transitions: Transition {
NumberAnimation {
properties: "width"
- duration: 300
+ duration: 175
}
}
-
}
}
}
+ // We use the contentItem property as a place to attach our window decorations. Beneath
+ // the usual menu entries within a MenuBar, it includes a centered information text, along
+ // with the minimize, maximize, and close buttons.
+ contentItem: RowLayout {
+ id: windowBar
- // The background property contains an information text in the middle as well as the
- // Minimize, Maximize and Close Buttons.
- background: Rectangle {
- color: Colors.surface2
- // Make the empty space drag the specified root window.
- WindowDragHandler { dragWindow: rootWindow }
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ spacing: root.spacing
+ Repeater {
+ id: menuBarItems
- Text {
- id: windowInfo
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.verticalCenter: parent.verticalCenter
- color: Colors.text
+ Layout.alignment: Qt.AlignLeft
+ model: root.contentModel
}
- component InteractionButton: Rectangle {
- signal action;
- property alias hovered: hoverHandler.hovered
+ Item {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Text {
+ id: windowInfo
+
+ width: parent.width; height: parent.height
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ leftPadding: windowActions.width
+ color: Colors.text
+ clip: true
+ }
+ }
- width: root.height
- anchors.top: parent.top
- anchors.bottom: parent.bottom
- color: hovered ? Colors.background : "transparent"
+ RowLayout {
+ id: windowActions
- HoverHandler { id: hoverHandler }
- TapHandler { onTapped: action() }
- }
+ Layout.alignment: Qt.AlignRight
+ Layout.fillHeight: true
- InteractionButton {
- id: minimize
+ spacing: 0
- anchors.right: maximize.left
- onAction: rootWindow.showMinimized()
- Rectangle {
- width: parent.height - 10; height: 2
- anchors.centerIn: parent
- color: parent.hovered ? Colors.iconIndicator : Colors.icon
+ component InteractionButton: Rectangle {
+ id: interactionButton
+
+ signal action()
+ property alias hovered: hoverHandler.hovered
+
+ Layout.fillHeight: true
+ Layout.preferredWidth: height
+
+ color: hovered ? Colors.background : "transparent"
+ HoverHandler {
+ id: hoverHandler
+ }
+ TapHandler {
+ id: tapHandler
+ onTapped: interactionButton.action()
+ }
}
- }
- InteractionButton {
- id: maximize
+ InteractionButton {
+ id: minimize
- anchors.right: close.left
- onAction: rootWindow.showMaximized()
- Rectangle {
- anchors.fill: parent
- anchors.margins: 5
- border.width: 2
- color: "transparent"
- border.color: parent.hovered ? Colors.iconIndicator : Colors.icon
+ onAction: root.dragWindow.showMinimized()
+ Rectangle {
+ anchors.centerIn: parent
+ color: parent.hovered ? Colors.iconIndicator : Colors.icon
+ height: 2
+ width: parent.height - 14
+ }
}
- }
- InteractionButton {
- id: close
+ InteractionButton {
+ id: maximize
- color: hovered ? "#ec4143" : "transparent"
- anchors.right: parent.right
- onAction: rootWindow.close()
- Rectangle {
- width: parent.height - 8; height: 2
- anchors.centerIn: parent
- color: parent.hovered ? Colors.iconIndicator : Colors.icon
- rotation: 45
- transformOrigin: Item.Center
- antialiasing: true
+ onAction: root.dragWindow.showMaximized()
+ Rectangle {
+ anchors.fill: parent
+ anchors.margins: 7
+ border.color: parent.hovered ? Colors.iconIndicator : Colors.icon
+ border.width: 2
+ color: "transparent"
+ }
+ }
+
+ InteractionButton {
+ id: close
+
+ color: hovered ? "#ec4143" : "transparent"
+ onAction: root.dragWindow.close()
Rectangle {
- width: parent.height
- height: parent.width
anchors.centerIn: parent
- color: parent.color
+ width: parent.height - 8; height: 2
+
+ rotation: 45
antialiasing: true
+ transformOrigin: Item.Center
+ color: parent.hovered ? Colors.iconIndicator : Colors.icon
+
+ Rectangle {
+ anchors.centerIn: parent
+ width: parent.height
+ height: parent.width
+
+ antialiasing: true
+ color: parent.color
+ }
}
}
}
}
+ background: Rectangle {
+ color: Colors.surface2
+ // Make the empty space drag the specified root window.
+ WindowDragHandler {
+ dragWindow: root.dragWindow
+ }
+ }
}
import FileSystemModule
Button {
+ required property ApplicationWindow resizeWindow
+
icon.width: 20; icon.height: 20
anchors.right: parent.right
anchors.bottom: parent.bottom
bottomPadding: 3
icon.source: "../icons/resize.svg"
- icon.color: down || checked ? Colors.iconIndicator : Colors.icon
+ icon.color: hovered ? Colors.iconIndicator : Colors.icon
+ background: null
checkable: false
display: AbstractButton.IconOnly
- background: null
- onPressed: {
- root.startSystemResize(Qt.BottomEdge | Qt.RightEdge)
- }
+ onPressed: resizeWindow.startSystemResize(Qt.BottomEdge | Qt.RightEdge)
}
Rectangle {
id: root
+
+ property alias currentTabIndex: topBar.currentIndex
+ required property ApplicationWindow dragWindow
+ readonly property int tabBarSpacing: 10
+
color: Colors.surface2
- required property ApplicationWindow rootWindow
- property alias currentTabIndex: tabBar.currentIndex
+ component SidebarEntry: Button {
+ id: sidebarButton
- ColumnLayout {
- anchors.fill: root
- anchors.topMargin: 10
- anchors.bottomMargin: 10
- spacing: 10
+ Layout.alignment: Qt.AlignHCenter
+ Layout.fillWidth: true
- // TabBar is designed to be horizontal, whereas we need a vertical bar.
- // We can easily achieve that by using a Container.
- Container {
- id: tabBar
+ icon.color: down || checked ? Colors.iconIndicator : Colors.icon
+ icon.width: 27
+ icon.height: 27
- Layout.fillWidth: true
+ topPadding: 0
+ rightPadding: 0
+ bottomPadding: 0
+ leftPadding: 0
+ background: null
- // ButtonGroup ensures that only one button can be checked at a time.
- ButtonGroup {
- buttons: tabBar.contentItem.children
- // We have to manage the currentIndex ourselves, which we do by setting it to the
- // index of the currently checked button.
- // We use setCurrentIndex instead of setting the currentIndex property to avoid breaking bindings.
- // See "Managing the Current Index" in Container's documentation for more information.
- onCheckedButtonChanged: tabBar.setCurrentIndex(Math.max(0, buttons.indexOf(checkedButton)))
- }
+ Rectangle {
+ id: indicator
- contentItem: ColumnLayout {
- spacing: tabBar.spacing
+ anchors.verticalCenter: parent.verticalCenter
+ x: 2
+ width: 4
+ height: sidebarButton.icon.height * 1.2
- Repeater {
- model: tabBar.contentModel
- }
- }
+ visible: sidebarButton.checked
+ color: Colors.color1
+ }
+ }
+
+ // TabBar is designed to be horizontal, whereas we need a vertical bar.
+ // We can easily achieve that by using a Container.
+ component TabBar: Container {
+ id: tabBarComponent
+
+ Layout.fillWidth: true
+ // ButtonGroup ensures that only one button can be checked at a time.
+ ButtonGroup {
+ buttons: tabBarComponent.contentChildren
+
+ // We have to manage the currentIndex ourselves, which we do by setting it to the index
+ // of the currently checked button. We use setCurrentIndex instead of setting the
+ // currentIndex property to avoid breaking bindings. See "Managing the Current Index"
+ // in Container's documentation for more information.
+ onCheckedButtonChanged: tabBarComponent.setCurrentIndex(
+ Math.max(0, buttons.indexOf(checkedButton)))
+ }
- component SidebarEntry: Button {
- id: sidebarButton
- icon.color: down || checked ? Colors.iconIndicator : Colors.icon
- icon.width: 35
- icon.height: 35
- leftPadding: 8 + indicator.width
-
- background: null
-
- Rectangle {
- id: indicator
- x: 4
- anchors.verticalCenter: parent.verticalCenter
- width: 4
- height: sidebarButton.icon.width
- color: Colors.color1
- visible: sidebarButton.checked
- }
+ contentItem: ColumnLayout {
+ spacing: tabBarComponent.spacing
+ Repeater {
+ model: tabBarComponent.contentModel
}
+ }
+ }
+ ColumnLayout {
+ anchors.fill: root
+ anchors.topMargin: root.tabBarSpacing
+ anchors.bottomMargin: root.tabBarSpacing
+
+ spacing: root.tabBarSpacing
+ TabBar {
+ id: topBar
+
+ spacing: root.tabBarSpacing
// Shows help text when clicked.
SidebarEntry {
+ id: infoTab
icon.source: "../icons/light_bulb.svg"
checkable: true
checked: true
-
- Layout.alignment: Qt.AlignHCenter
}
// Shows the file system when clicked.
SidebarEntry {
+ id: filesystemTab
+
icon.source: "../icons/read.svg"
checkable: true
-
- Layout.alignment: Qt.AlignHCenter
}
}
Layout.fillWidth: true
// Make the empty space drag our main window.
- WindowDragHandler { dragWindow: rootWindow }
+ WindowDragHandler {
+ dragWindow: root.dragWindow
+ }
}
- // Opens the Qt website in the system's web browser.
- SidebarEntry {
- id: qtWebsiteButton
- icon.source: "../icons/globe.svg"
- checkable: false
+ TabBar {
+ id: bottomBar
- onClicked: Qt.openUrlExternally("https://www.qt.io/")
- }
+ spacing: root.tabBarSpacing
+ // Opens the Qt website in the system's web browser.
+ SidebarEntry {
+ id: qtWebsiteButton
+ icon.source: "../icons/globe.svg"
+ checkable: false
+ onClicked: Qt.openUrlExternally("https://www.qt.io/")
+ }
- // Opens the About Qt Window.
- SidebarEntry {
- id: aboutQtButton
- icon.source: "../icons/info_sign.svg"
- checkable: false
+ // Opens the About Qt Window.
+ SidebarEntry {
+ id: aboutQtButton
- onClicked: aboutQtWindow.visible = !aboutQtWindow.visible
+ icon.source: "../icons/info_sign.svg"
+ checkable: false
+ onClicked: aboutQtWindow.visible = !aboutQtWindow.visible
+ }
}
}
module FileSystemModule
Main 1.0 Main.qml
-Icon 1.0 qml/Icon.qml
About 1.0 qml/About.qml
+Editor 1.0 qml/Editor.qml
MyMenu 1.0 qml/MyMenu.qml
Sidebar 1.0 qml/Sidebar.qml
MyMenuBar 1.0 qml/MyMenuBar.qml
.. image:: filesystemexplorer.webp
:target: filesystemexplorer.webp
:alt: QtQuickControls Filesystem Explorer Screenshot
+
+References
+----------
+
+If you're interested in the C++ version of this example, you can find it
+`here <https://doc-snapshots.qt.io/qt6-dev/qtquickcontrols-filesystemexplorer-example.html>`_.
+
+Additionally, there is a detailed
+`tutorial <https://doc.qt.io/qtforpython-6/tutorials/extendedexplorer/extendedexplorer.html>`_
+available that provides step-by-step instructions on how to extend this example
+with additional features. This tutorial can be helpful if you want to explore
+and learn more about building upon the existing functionality of the filesystem
+explorer.
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtWidgets import QFileSystemModel
+from PySide6.QtQuick import QQuickTextDocument
+from PySide6.QtQml import QmlElement, QmlSingleton
+from PySide6.QtCore import (Qt, QDir, QAbstractListModel, Slot, QFile, QTextStream,
+ QMimeDatabase, QFileInfo, QStandardPaths, QModelIndex,
+ Signal, Property)
+
+QML_IMPORT_NAME = "FileSystemModule"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+@QmlSingleton
+class FileSystemModel(QFileSystemModel):
+
+ rootIndexChanged = Signal()
+
+ def getDefaultRootDir():
+ return QStandardPaths.writableLocation(QStandardPaths.StandardLocation.HomeLocation)
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+ self.mRootIndex = QModelIndex()
+ self.mDb = QMimeDatabase()
+ self.setFilter(QDir.Filter.AllEntries | QDir.Filter.Hidden | QDir.Filter.NoDotAndDotDot)
+ self.setInitialDirectory()
+
+ # check for the correct mime type and then read the file.
+ # returns the text file's content or an error message on failure
+ @Slot(str, result=str)
+ def readFile(self, path):
+ if path == "":
+ return ""
+
+ file = QFile(path)
+
+ mime = self.mDb.mimeTypeForFile(QFileInfo(file))
+ if ('text' in mime.comment().lower()
+ or any('text' in s.lower() for s in mime.parentMimeTypes())):
+ if file.open(QFile.OpenModeFlag.ReadOnly | QFile.OpenModeFlag.Text):
+ stream = QTextStream(file).readAll()
+ file.close()
+ return stream
+ else:
+ return self.tr("Error opening the file!")
+ return self.tr("File type not supported!")
+
+ @Slot(QQuickTextDocument, int, result=int)
+ def currentLineNumber(self, textDocument, cursorPosition):
+ td = textDocument.textDocument()
+ tb = td.findBlock(cursorPosition)
+ return tb.blockNumber()
+
+ def setInitialDirectory(self, path=getDefaultRootDir()):
+ dir = QDir(path)
+ if dir.makeAbsolute():
+ self.setRootPath(dir.path())
+ else:
+ self.setRootPath(self.getDefaultRootDir())
+ self.setRootIndex(self.index(dir.path()))
+
+ # we only need one column in this example
+ def columnCount(self, parent):
+ return 1
+
+ @Property(QModelIndex, notify=rootIndexChanged)
+ def rootIndex(self):
+ return self.mRootIndex
+
+ def setRootIndex(self, index):
+ if (index == self.mRootIndex):
+ return
+ self.mRootIndex = index
+ self.rootIndexChanged.emit()
+
+
+@QmlElement
+class LineNumberModel(QAbstractListModel):
+
+ lineCountChanged = Signal()
+
+ def __init__(self, parent=None):
+ self.mLineCount = 0
+ super().__init__(parent=parent)
+
+ @Property(int, notify=lineCountChanged)
+ def lineCount(self):
+ return self.mLineCount
+
+ @lineCount.setter
+ def lineCount(self, n):
+ if n < 0:
+ print("lineCount must be greater then zero")
+ return
+ if self.mLineCount == n:
+ return
+
+ if self.mLineCount < n:
+ self.beginInsertRows(QModelIndex(), self.mLineCount, n - 1)
+ self.mLineCount = n
+ self.endInsertRows()
+ else:
+ self.beginRemoveRows(QModelIndex(), n, self.mLineCount - 1)
+ self.mLineCount = n
+ self.endRemoveRows()
+
+ def rowCount(self, parent):
+ return self.mLineCount
+
+ def data(self, index, role):
+ if not self.checkIndex(index) or role != Qt.ItemDataRole.DisplayRole:
+ return
+ return index.row()
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-"""
-This example shows how to customize Qt Quick Controls by implementing a simple filesystem explorer.
-"""
-
-# Compile both resource files app.qrc and icons.qrc and include them here if you wish
-# to load them from the resource system. Currently, all resources are loaded locally
-# import FileSystemModule.rc_icons
-# import FileSystemModule.rc_app
-
-from PySide6.QtWidgets import QFileSystemModel
-from PySide6.QtGui import QGuiApplication
-from PySide6.QtQml import (QQmlApplicationEngine, QmlElement, QmlSingleton)
-from PySide6.QtCore import (Slot, QFile, QTextStream, QMimeDatabase, QFileInfo, QStandardPaths)
-
-import sys
-
-
-QML_IMPORT_NAME = "FileSystemModule"
-QML_IMPORT_MAJOR_VERSION = 1
-
-
-@QmlElement
-@QmlSingleton
-class FileSystemModel(QFileSystemModel):
- def __init__(self, parent=None):
- super().__init__(parent=parent)
- self.setRootPath(QStandardPaths.writableLocation(QStandardPaths.HomeLocation))
- self.db = QMimeDatabase()
-
- # we only need one column in this example
- def columnCount(self, parent):
- return 1
-
- # check for the correct mime type and then read the file.
- # returns the text file's content or an error message on failure
- @Slot(str, result=str)
- def readFile(self, path):
- if path == "":
- return ""
-
- file = QFile(path)
-
- mime = self.db.mimeTypeForFile(QFileInfo(file))
- if ('text' in mime.comment().lower()
- or any('text' in s.lower() for s in mime.parentMimeTypes())):
- if file.open(QFile.ReadOnly | QFile.Text):
- stream = QTextStream(file).readAll()
- return stream
- else:
- return self.tr("Error opening the file!")
- return self.tr("File type not supported!")
-
-
-if __name__ == '__main__':
- app = QGuiApplication(sys.argv)
- app.setOrganizationName("QtProject")
- app.setApplicationName("File System Explorer")
- engine = QQmlApplicationEngine()
- # Include the path of this file to search for the 'qmldir' module
- engine.addImportPath(sys.path[0])
-
- engine.loadFromModule("FileSystemModule", "Main")
-
- if not engine.rootObjects():
- sys.exit(-1)
-
- sys.exit(app.exec())
{
"files": [
- "filesystemexplorer.py",
+ "main.py",
+ "editormodels.py",
"FileSystemModule/qmldir",
"FileSystemModule/app.qrc",
"FileSystemModule/qmldir",
"FileSystemModule/Main.qml",
"FileSystemModule/qml/About.qml",
"FileSystemModule/qml/Colors.qml",
+ "FileSystemModule/qml/Editor.qml",
"FileSystemModule/qml/FileSystemView.qml",
- "FileSystemModule/qml/Icon.qml",
"FileSystemModule/qml/MyMenu.qml",
"FileSystemModule/qml/MyMenuBar.qml",
"FileSystemModule/qml/ResizeButton.qml",
"FileSystemModule/qml/Sidebar.qml",
"FileSystemModule/qml/WindowDragHandler.qml",
+ "FileSystemModule/icons/app_icon.svg",
"FileSystemModule/icons/folder_closed.svg",
"FileSystemModule/icons/folder_open.svg",
"FileSystemModule/icons/generic_file.svg",
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+"""
+This example shows how to customize Qt Quick Controls by implementing a simple filesystem explorer.
+"""
+
+# Compile both resource files app.qrc and icons.qrc and include them here if you wish
+# to load them from the resource system. Currently, all resources are loaded locally
+# import FileSystemModule.rc_icons
+# import FileSystemModule.rc_app
+
+from editormodels import FileSystemModel # noqa: F401
+from PySide6.QtGui import QGuiApplication, QIcon
+from PySide6.QtQml import QQmlApplicationEngine
+from PySide6.QtCore import QCommandLineParser, qVersion
+
+import sys
+
+if __name__ == '__main__':
+ app = QGuiApplication(sys.argv)
+ app.setOrganizationName("QtProject")
+ app.setApplicationName("File System Explorer")
+ app.setApplicationVersion(qVersion())
+ app.setWindowIcon(QIcon(sys.path[0] + "/FileSystemModule/icons/app_icon.svg"))
+
+ parser = QCommandLineParser()
+ parser.setApplicationDescription("Qt Filesystemexplorer Example")
+ parser.addHelpOption()
+ parser.addVersionOption()
+ parser.addPositionalArgument("", "Initial directory", "[path]")
+ parser.process(app)
+ args = parser.positionalArguments()
+
+ engine = QQmlApplicationEngine()
+ # Include the path of this file to search for the 'qmldir' module
+ engine.addImportPath(sys.path[0])
+
+ engine.loadFromModule("FileSystemModule", "Main")
+
+ if not engine.rootObjects():
+ sys.exit(-1)
+
+ if (len(args) == 1):
+ fsm = engine.singletonInstance("FileSystemModule", "FileSystemModel")
+ fsm.setInitialDirectory(args[0])
+
+ sys.exit(app.exec())
You can build and run this example by executing the following commands
(slightly adapted to your file system layout) in a terminal:
-macOS/Linux:
+Run CMake on macOS/Linux:
.. code-block:: bash
cd ~/pyside-setup/examples/samplebinding
+ mkdir build
+ cd build
+ cmake .. -B. -G Ninja -DCMAKE_BUILD_TYPE=Release
-On Windows:
+Run CMake on Windows:
.. code-block:: bash
cd C:\pyside-setup\examples\samplebinding
-
-.. code-block:: bash
-
mkdir build
cd build
- cmake -H.. -B. -G Ninja -DCMAKE_BUILD_TYPE=Release
+ cmake .. -B. -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=cl.exe
+
+To build:
+
+.. code-block:: bash
ninja
ninja install
cd ..
.. code-block:: bash
- cmake -H.. -B. -DCMAKE_C_COMPILER=cl.exe -DCMAKE_CXX_COMPILER=cl.exe
+ cmake -S.. -B. -DCMAKE_C_COMPILER=cl.exe -DCMAKE_CXX_COMPILER=cl.exe
or by using the -G option:
.. code-block:: bash
- cmake -H.. -B. -G "Visual Studio 14 Win64"
+ cmake -S.. -B. -G "Visual Studio 14 Win64"
If the ``-G "Visual Studio 14 Win64"`` option is used, a ``sln`` file
will be generated, and can be used with ``MSBuild``
.. code-block:: bash
cd ~/pyside-setup/examples/scriptableapplication
+ mkdir build
+ cd build
+ cmake .. -B. -G Ninja -DCMAKE_BUILD_TYPE=Release
+ ninja
+ ./scriptableapplication
On Windows:
.. code-block:: bash
cd C:\pyside-setup\examples\scriptableapplication
-
-
-.. code-block:: bash
-
mkdir build
cd build
- cmake -H.. -B. -G Ninja -DCMAKE_BUILD_TYPE=Release
+ cmake .. -B. -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=cl.exe
ninja
- ./scriptableapplication
+ .\scriptableapplication.exe
Using QMake
+++++++++++
.. code-block:: bash
- cmake -H.. -B. -DCMAKE_C_COMPILER=cl.exe -DCMAKE_CXX_COMPILER=cl.exe
+ cmake -S.. -B. -DCMAKE_C_COMPILER=cl.exe -DCMAKE_CXX_COMPILER=cl.exe
or using the -G option:
.. code-block:: bash
- cmake -H.. -B. -G "Visual Studio 14 Win64" -DCMAKE_BUILD_TYPE=Release
+ cmake -S.. -B. -G "Visual Studio 14 Win64" -DCMAKE_BUILD_TYPE=Release
If the ``-G "Visual Studio 14 Win64"`` option is used, a ``sln`` file
diagnosticAction->setShortcut(Qt::CTRL | Qt::Key_D);
fileMenu->addAction(tr("&Invoke testFunction1()"),
this, &MainWindow::testFunction1);
- const QIcon quitIcon = QIcon::fromTheme("application-exit"_L1);
+ const QIcon quitIcon = QIcon::fromTheme(QIcon::ThemeIcon::ApplicationExit);
auto *quitAction = fileMenu->addAction(quitIcon, tr("&Quit"),
qApp, &QCoreApplication::quit);
quitAction->setShortcut(Qt::CTRL | Qt::Key_Q);
auto *editMenu = menuBar()->addMenu(tr("&Edit"));
- const QIcon clearIcon = QIcon::fromTheme("edit-clear"_L1);
+ const QIcon clearIcon = QIcon::fromTheme(QIcon::ThemeIcon::EditClear);
auto *clearAction = editMenu->addAction(clearIcon, tr("&Clear"),
m_scriptEdit, &QPlainTextEdit::clear);
auto *helpMenu = menuBar()->addMenu(tr("&Help"));
- const QIcon aboutIcon = QIcon::fromTheme("help-about"_L1);
+ const QIcon aboutIcon = QIcon::fromTheme(QIcon::ThemeIcon::HelpAbout);
auto *aboutAction = helpMenu->addAction(aboutIcon, tr("&About Qt"),
qApp, &QApplication::aboutQt);
#include <QtWidgets/QMainWindow>
-class QPlainTextEdit;
+QT_FORWARD_DECLARE_CLASS(QPlainTextEdit)
class MainWindow : public QMainWindow
{
void testFunction1();
+ static constexpr auto TEST = QLatin1StringView("test");
+
private Q_SLOTS:
void slotRunScript();
void slotPrintDiagnostics();
#include <QtCore/QStringList>
-class QObject;
+QT_FORWARD_DECLARE_CLASS(QObject)
namespace PythonUtils {
################################################################################
## Form generated from reading UI file 'canbusdeviceinfobox.ui'
##
-## Created by: Qt User Interface Compiler version 6.4.1
+## Created by: Qt User Interface Compiler version 6.7.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
################################################################################
## Form generated from reading UI file 'canbusdeviceinfodialog.ui'
##
-## Created by: Qt User Interface Compiler version 6.4.1
+## Created by: Qt User Interface Compiler version 6.7.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self.horizontalLayout = QHBoxLayout()
self.horizontalLayout.setObjectName(u"horizontalLayout")
- self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
+ self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
self.horizontalLayout.addItem(self.horizontalSpacer)
################################################################################
## Form generated from reading UI file 'connectdialog.ui'
##
-## Created by: Qt User Interface Compiler version 6.4.1
+## Created by: Qt User Interface Compiler version 6.7.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self.horizontalLayout = QHBoxLayout()
self.horizontalLayout.setObjectName(u"horizontalLayout")
- self.horizontalSpacer = QSpacerItem(96, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
+ self.horizontalSpacer = QSpacerItem(96, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
self.horizontalLayout.addItem(self.horizontalSpacer)
################################################################################
## Form generated from reading UI file 'mainwindow.ui'
##
-## Created by: Qt User Interface Compiler version 6.4.1
+## Created by: Qt User Interface Compiler version 6.7.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self.horizontalLayout.addWidget(self.busStatus)
- self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
+ self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
self.horizontalLayout.addItem(self.horizontalSpacer)
MainWindow.setMenuBar(self.menuBar)
self.mainToolBar = QToolBar(MainWindow)
self.mainToolBar.setObjectName(u"mainToolBar")
- MainWindow.addToolBar(Qt.TopToolBarArea, self.mainToolBar)
+ MainWindow.addToolBar(Qt.ToolBarArea.TopToolBarArea, self.mainToolBar)
self.statusBar = QStatusBar(MainWindow)
self.statusBar.setObjectName(u"statusBar")
MainWindow.setStatusBar(self.statusBar)
################################################################################
## Form generated from reading UI file 'sendframebox.ui'
##
-## Created by: Qt User Interface Compiler version 6.4.1
+## Created by: Qt User Interface Compiler version 6.7.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self.frameIdEdit = QLineEdit(SendFrameBox)
self.frameIdEdit.setObjectName(u"frameIdEdit")
- sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
+ sizePolicy = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
sizePolicy.setHorizontalStretch(1)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.frameIdEdit.sizePolicy().hasHeightForWidth())
self.payloadEdit = QLineEdit(SendFrameBox)
self.payloadEdit.setObjectName(u"payloadEdit")
- sizePolicy1 = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
+ sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
sizePolicy1.setHorizontalStretch(2)
sizePolicy1.setVerticalStretch(0)
sizePolicy1.setHeightForWidth(self.payloadEdit.sizePolicy().hasHeightForWidth())
################################################################################
## Form generated from reading UI file 'mainwindow.ui'
##
-## Created by: Qt User Interface Compiler version 6.4.1
+## Created by: Qt User Interface Compiler version 6.7.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self.gridLayout.setObjectName(u"gridLayout")
self.label_27 = QLabel(self.centralWidget)
self.label_27.setObjectName(u"label_27")
- sizePolicy = QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Preferred)
+ sizePolicy = QSizePolicy(QSizePolicy.Policy.Maximum, QSizePolicy.Policy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.label_27.sizePolicy().hasHeightForWidth())
self.connectButton = QPushButton(self.centralWidget)
self.connectButton.setObjectName(u"connectButton")
- sizePolicy1 = QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Fixed)
+ sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Maximum, QSizePolicy.Policy.Fixed)
sizePolicy1.setHorizontalStretch(0)
sizePolicy1.setVerticalStretch(0)
sizePolicy1.setHeightForWidth(self.connectButton.sizePolicy().hasHeightForWidth())
self.gridLayout.addWidget(self.connectButton, 0, 7, 1, 1)
- self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
+ self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
self.gridLayout.addItem(self.horizontalSpacer, 0, 4, 1, 1)
self.portEdit = QLineEdit(self.centralWidget)
self.portEdit.setObjectName(u"portEdit")
- sizePolicy2 = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
+ sizePolicy2 = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
sizePolicy2.setHorizontalStretch(0)
sizePolicy2.setVerticalStretch(0)
sizePolicy2.setHeightForWidth(self.portEdit.sizePolicy().hasHeightForWidth())
self.horizontalLayout.addWidget(self.writeTable)
- self.horizontalSpacer_2 = QSpacerItem(13, 17, QSizePolicy.Expanding, QSizePolicy.Minimum)
+ self.horizontalSpacer_2 = QSpacerItem(13, 17, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
self.horizontalLayout.addItem(self.horizontalSpacer_2)
################################################################################
## Form generated from reading UI file 'settingsdialog.ui'
##
-## Created by: Qt User Interface Compiler version 6.4.1
+## Created by: Qt User Interface Compiler version 6.7.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
SettingsDialog.resize(239, 256)
self.gridLayout = QGridLayout(SettingsDialog)
self.gridLayout.setObjectName(u"gridLayout")
- self.verticalSpacer = QSpacerItem(20, 43, QSizePolicy.Minimum, QSizePolicy.Expanding)
+ self.verticalSpacer = QSpacerItem(20, 43, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
self.gridLayout.addItem(self.verticalSpacer, 3, 1, 1, 1)
################################################################################
## Form generated from reading UI file 'mainwindow.ui'
##
-## Created by: Qt User Interface Compiler version 6.4.0
+## Created by: Qt User Interface Compiler version 6.7.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
MainWindow.setMenuBar(self.menuBar)
self.mainToolBar = QToolBar(MainWindow)
self.mainToolBar.setObjectName(u"mainToolBar")
- MainWindow.addToolBar(Qt.TopToolBarArea, self.mainToolBar)
+ MainWindow.addToolBar(Qt.ToolBarArea.TopToolBarArea, self.mainToolBar)
self.statusBar = QStatusBar(MainWindow)
self.statusBar.setObjectName(u"statusBar")
MainWindow.setStatusBar(self.statusBar)
################################################################################
## Form generated from reading UI file 'settingsdialog.ui'
##
-## Created by: Qt User Interface Compiler version 6.4.0
+## Created by: Qt User Interface Compiler version 6.7.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self.horizontalLayout = QHBoxLayout()
self.horizontalLayout.setObjectName(u"horizontalLayout")
- self.horizontalSpacer = QSpacerItem(96, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
+ self.horizontalSpacer = QSpacerItem(96, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
self.horizontalLayout.addItem(self.horizontalSpacer)
################################################################################
## Form generated from reading UI file 'mainwindow.ui'
##
-## Created by: Qt User Interface Compiler version 6.5.0
+## Created by: Qt User Interface Compiler version 6.7.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self.verticalLayout.setObjectName(u"verticalLayout")
self.plainTextEdit = QPlainTextEdit(self.centralwidget)
self.plainTextEdit.setObjectName(u"plainTextEdit")
- sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ sizePolicy = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.plainTextEdit.sizePolicy().hasHeightForWidth())
self.gridLayout.setObjectName(u"gridLayout")
self.label_5 = QLabel(self.centralwidget)
self.label_5.setObjectName(u"label_5")
- sizePolicy1 = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
+ sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Preferred)
sizePolicy1.setHorizontalStretch(0)
sizePolicy1.setVerticalStretch(0)
sizePolicy1.setHeightForWidth(self.label_5.sizePolicy().hasHeightForWidth())
self.language = QComboBox(self.centralwidget)
self.language.setObjectName(u"language")
- sizePolicy2 = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
+ sizePolicy2 = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
sizePolicy2.setHorizontalStretch(0)
sizePolicy2.setVerticalStretch(0)
sizePolicy2.setHeightForWidth(self.language.sizePolicy().hasHeightForWidth())
self.verticalLayout.addLayout(self.horizontalLayout)
- self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
+ self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
self.verticalLayout.addItem(self.verticalSpacer)
################################################################################
## Form generated from reading UI file 'bookwindow.ui'
##
-## Created by: Qt User Interface Compiler version 6.2.3
+## Created by: Qt User Interface Compiler version 6.7.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
return None
-# Return version as "3.7"
+# Return version as "x.y" (e.g. 3.9, 3.12, etc)
def python_version():
return str(sys.version_info[0]) + '.' + str(sys.version_info[1])
################################################################################
## Form generated from reading UI file 'dialog.ui'
##
-## Created by: Qt User Interface Compiler version 6.2.3
+## Created by: Qt User Interface Compiler version 6.7.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
################################################################################
## Form generated from reading UI file 'mainwindow.ui'
##
-## Created by: Qt User Interface Compiler version 6.2.3
+## Created by: Qt User Interface Compiler version 6.7.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self._profile = profile
self._tab_widget = TabWidget(profile, self)
- self._stop_icon = QIcon(":process-stop.png")
- self._reload_icon = QIcon(":view-refresh.png")
+ self._stop_icon = QIcon.fromTheme(QIcon.ThemeIcon.ProcessStop,
+ QIcon(":process-stop.png"))
+ self._reload_icon = QIcon.fromTheme(QIcon.ThemeIcon.ViewRefresh,
+ QIcon(":view-refresh.png"))
self.setAttribute(Qt.WA_DeleteOnClose, True)
self.setFocusPolicy(Qt.ClickFocus)
back_shortcuts.append(QKeySequence(Qt.Key_Back))
self._history_back_action.setShortcuts(back_shortcuts)
self._history_back_action.setIconVisibleInMenu(False)
- self._history_back_action.setIcon(QIcon(":go-previous.png"))
+ back_icon = QIcon.fromTheme(QIcon.ThemeIcon.GoPrevious,
+ QIcon(":go-previous.png"))
+ self._history_back_action.setIcon(back_icon)
self._history_back_action.setToolTip("Go back in history")
self._history_back_action.triggered.connect(self._back)
navigation_bar.addAction(self._history_back_action)
fwd_shortcuts.append(QKeySequence(Qt.Key_Forward))
self._history_forward_action.setShortcuts(fwd_shortcuts)
self._history_forward_action.setIconVisibleInMenu(False)
- self._history_forward_action.setIcon(QIcon(":go-next.png"))
+ next_icon = QIcon.fromTheme(QIcon.ThemeIcon.GoNext,
+ QIcon(":go-next.png"))
+ self._history_forward_action.setIcon(next_icon)
self._history_forward_action.setToolTip("Go forward in history")
self._history_forward_action.triggered.connect(self._forward)
navigation_bar.addAction(self._history_forward_action)
self._download = download
self._time_added = QElapsedTimer()
self._time_added.start()
- self._cancel_icon = QIcon(":process-stop.png")
- self._remove_icon = QIcon(":edit-clear.png")
+ self._cancel_icon = QIcon.fromTheme(QIcon.ThemeIcon.ProcessStop,
+ QIcon(":process-stop.png"))
+ self._remove_icon = QIcon.fromTheme(QIcon.ThemeIcon.EditClear,
+ QIcon(":edit-clear.png"))
self._ui = Ui_DownloadWidget()
self._ui.setupUi(self)
################################################################################
## Form generated from reading UI file 'certificateerrordialog.ui'
##
-## Created by: Qt User Interface Compiler version 6.5.0
+## Created by: Qt User Interface Compiler version 6.7.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self.m_errorLabel = QLabel(CertificateErrorDialog)
self.m_errorLabel.setObjectName(u"m_errorLabel")
- sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
+ sizePolicy = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.m_errorLabel.sizePolicy().hasHeightForWidth())
self.m_infoLabel = QLabel(CertificateErrorDialog)
self.m_infoLabel.setObjectName(u"m_infoLabel")
- sizePolicy1 = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)
+ sizePolicy1 = QSizePolicy(QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.MinimumExpanding)
sizePolicy1.setHorizontalStretch(0)
sizePolicy1.setVerticalStretch(0)
sizePolicy1.setHeightForWidth(self.m_infoLabel.sizePolicy().hasHeightForWidth())
self.verticalLayout.addWidget(self.m_infoLabel)
- self.verticalSpacer = QSpacerItem(20, 16, QSizePolicy.Minimum, QSizePolicy.Expanding)
+ self.verticalSpacer = QSpacerItem(20, 16, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
self.verticalLayout.addItem(self.verticalSpacer)
################################################################################
## Form generated from reading UI file 'downloadmanagerwidget.ui'
##
-## Created by: Qt User Interface Compiler version 6.5.0
+## Created by: Qt User Interface Compiler version 6.7.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self.m_itemsLayout.setContentsMargins(3, 3, 3, 3)
self.m_zeroItemsLabel = QLabel(self.m_items)
self.m_zeroItemsLabel.setObjectName(u"m_zeroItemsLabel")
- sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ sizePolicy = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.m_zeroItemsLabel.sizePolicy().hasHeightForWidth())
################################################################################
## Form generated from reading UI file 'downloadwidget.ui'
##
-## Created by: Qt User Interface Compiler version 6.5.0
+## Created by: Qt User Interface Compiler version 6.7.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self.m_cancelButton = QPushButton(DownloadWidget)
self.m_cancelButton.setObjectName(u"m_cancelButton")
- sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
+ sizePolicy = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.m_cancelButton.sizePolicy().hasHeightForWidth())
################################################################################
## Form generated from reading UI file 'passworddialog.ui'
##
-## Created by: Qt User Interface Compiler version 6.5.0
+## Created by: Qt User Interface Compiler version 6.7.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self.m_infoLabel = QLabel(PasswordDialog)
self.m_infoLabel.setObjectName(u"m_infoLabel")
- sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
+ sizePolicy = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.m_infoLabel.sizePolicy().hasHeightForWidth())
self.renderProcessTerminated.connect(self._render_process_terminated)
self._error_icon = QIcon(":dialog-error.png")
- self._loading_icon = QIcon(":view-refresh.png")
+ self._loading_icon = QIcon.fromTheme(QIcon.ThemeIcon.ViewRefresh,
+ QIcon(":view-refresh.png"))
self._default_icon = QIcon(":text-html.png")
@Slot()
-# WigglyWidget
-
-The original Qt/C++ example can be found here:
-https://doc.qt.io/qt-6/qtwidgets-widgets-wiggly-example.html
+(widgetbinding-example)=
+# WigglyWidget Example
This example shows how to interact with a custom widget from two
different ways:
Now create a `build/` directory, and from inside run `cmake` to use
the provided `CMakeLists.txt`:
-macOS/Linux:
+Run CMake on macOS/Linux:
```bash
cd ~/pyside-setup/examples/widgetbinding
+cd build
+cmake .. -B. -G Ninja -DCMAKE_BUILD_TYPE=Release
```
-On Windows:
+Run CMake on Windows:
```bash
cd C:\pyside-setup\examples\widgetbinding
+mkdir build
+cd build
+cmake .. -B. -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=cl.exe
```
+To build:
```bash
-mkdir build
-cd build
-cmake -H.. -B. -G Ninja -DCMAKE_BUILD_TYPE=Release
+
ninja
ninja install
cd ..
# -*- coding: utf-8 -*-
################################################################################
-## Form generated from reading UI file 'form2.ui'
+## Form generated from reading UI file 'form.ui'
##
-## Created by: Qt User Interface Compiler version 6.4.0
+## Created by: Qt User Interface Compiler version 6.7.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self.gridLayout.setObjectName(u"gridLayout")
self.easingCurvePicker = QListWidget(Form)
self.easingCurvePicker.setObjectName(u"easingCurvePicker")
- sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
+ sizePolicy = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.easingCurvePicker.sizePolicy().hasHeightForWidth())
self.groupBox = QGroupBox(Form)
self.groupBox.setObjectName(u"groupBox")
- sizePolicy1 = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)
+ sizePolicy1 = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Preferred)
sizePolicy1.setHorizontalStretch(0)
sizePolicy1.setVerticalStretch(0)
sizePolicy1.setHeightForWidth(self.groupBox.sizePolicy().hasHeightForWidth())
self.formLayout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
self.label = QLabel(self.groupBox)
self.label.setObjectName(u"label")
- sizePolicy2 = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
+ sizePolicy2 = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred)
sizePolicy2.setHorizontalStretch(0)
sizePolicy2.setVerticalStretch(0)
sizePolicy2.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
self.periodSpinBox = QDoubleSpinBox(self.groupBox)
self.periodSpinBox.setObjectName(u"periodSpinBox")
self.periodSpinBox.setEnabled(False)
- sizePolicy3 = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
+ sizePolicy3 = QSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed)
sizePolicy3.setHorizontalStretch(0)
sizePolicy3.setVerticalStretch(0)
sizePolicy3.setHeightForWidth(self.periodSpinBox.sizePolicy().hasHeightForWidth())
self.verticalLayout.addWidget(self.groupBox)
- self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
+ self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
self.verticalLayout.addItem(self.verticalSpacer)
self.graphicsView = QGraphicsView(Form)
self.graphicsView.setObjectName(u"graphicsView")
- sizePolicy4 = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ sizePolicy4 = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
sizePolicy4.setHorizontalStretch(0)
sizePolicy4.setVerticalStretch(0)
sizePolicy4.setHeightForWidth(self.graphicsView.sizePolicy().hasHeightForWidth())
<!DOCTYPE RCC><RCC version="1.0">
-<qresource>
- <file>translations/example_de.qm</file>
+<qresource prefix="translations">
+ <file>example_de.qm</file>
</qresource>
</RCC>
self.setWindowModified(self._text_edit.document().isModified())
def create_actions(self):
- icon = QIcon.fromTheme("document-new", QIcon(':/images/new.png'))
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentNew, QIcon(':/images/new.png'))
self._new_act = QAction(icon, "&New", self, shortcut=QKeySequence.New,
statusTip="Create a new file", triggered=self.new_file)
- icon = QIcon.fromTheme("document-open", QIcon(':/images/open.png'))
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentOpen, QIcon(':/images/open.png'))
self._open_act = QAction(icon, "&Open...", self,
shortcut=QKeySequence.Open, statusTip="Open an existing file",
triggered=self.open)
- icon = QIcon.fromTheme("document-save", QIcon(':/images/save.png'))
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentSave, QIcon(':/images/save.png'))
self._save_act = QAction(icon, "&Save", self,
shortcut=QKeySequence.Save,
statusTip="Save the document to disk", triggered=self.save)
statusTip="Save the document under a new name",
triggered=self.save_as)
- self._exit_act = QAction("E&xit", self, shortcut="Ctrl+Q",
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.ApplicationExit)
+ self._exit_act = QAction(icon, "E&xit", self, shortcut="Ctrl+Q",
statusTip="Exit the application", triggered=self.close)
- icon = QIcon.fromTheme("edit-cut", QIcon(':/images/cut.png'))
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.EditCut, QIcon(':/images/cut.png'))
self._cut_act = QAction(icon, "Cu&t", self, shortcut=QKeySequence.Cut,
statusTip="Cut the current selection's contents to the clipboard",
triggered=self._text_edit.cut)
- icon = QIcon.fromTheme("edit-copy", QIcon(':/images/copy.png'))
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.EditCopy, QIcon(':/images/copy.png'))
self._copy_act = QAction(icon, "&Copy",
self, shortcut=QKeySequence.Copy,
statusTip="Copy the current selection's contents to the clipboard",
triggered=self._text_edit.copy)
- icon = QIcon.fromTheme("edit-paste", QIcon(':/images/paste.png'))
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.EditPaste, QIcon(':/images/paste.png'))
self._paste_act = QAction(icon, "&Paste",
self, shortcut=QKeySequence.Paste,
statusTip="Paste the clipboard's contents into the current "
"selection",
triggered=self._text_edit.paste)
- self._about_act = QAction("&About", self,
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.HelpAbout)
+ self._about_act = QAction(icon, "&About", self,
statusTip="Show the application's About box",
triggered=self.about)
def create_actions(self):
- icon = QIcon.fromTheme("document-new")
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentNew)
self._new_act = QAction(icon, "&New", self,
shortcut=QKeySequence.New, statusTip="Create a new file",
triggered=self.new_file)
- icon = QIcon.fromTheme("document-open")
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentOpen)
self._open_act = QAction(icon, "&Open...", self,
shortcut=QKeySequence.Open, statusTip="Open an existing file",
triggered=self.open)
- icon = QIcon.fromTheme("document-save")
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentSave)
self._save_act = QAction(icon, "&Save", self,
shortcut=QKeySequence.Save,
statusTip="Save the document to disk", triggered=self.save)
statusTip="Save the document under a new name",
triggered=self.save_as)
- self._exit_act = QAction("E&xit", self, shortcut=QKeySequence.Quit,
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.ApplicationExit)
+ self._exit_act = QAction(icon, "E&xit", self, shortcut=QKeySequence.Quit,
statusTip="Exit the application",
triggered=QApplication.instance().closeAllWindows)
- icon = QIcon.fromTheme("edit-cut")
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.EditCut)
self._cut_act = QAction(icon, "Cu&t", self,
shortcut=QKeySequence.Cut,
statusTip="Cut the current selection's contents to the clipboard",
triggered=self.cut)
- icon = QIcon.fromTheme("edit-copy")
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.EditCopy)
self._copy_act = QAction(icon, "&Copy", self,
shortcut=QKeySequence.Copy,
statusTip="Copy the current selection's contents to the clipboard",
triggered=self.copy)
- icon = QIcon.fromTheme("edit-paste")
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.EditPaste)
self._paste_act = QAction(icon, "&Paste", self,
shortcut=QKeySequence.Paste,
statusTip="Paste the clipboard's contents into the current "
self._separator_act = QAction(self)
self._separator_act.setSeparator(True)
- self._about_act = QAction("&About", self,
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.HelpAbout)
+ self._about_act = QAction(icon, "&About", self,
statusTip="Show the application's About box",
triggered=self.about)
--- /dev/null
+.. _rhi-widget-example:
+
+Simple RHI Widget Example
+=========================
+
+Shows how to render a triangle using ``QRhi``, Qt's 3D API and shading
+language abstraction layer.
+
+This example is, in many ways, the counterpart of the :ref:`rhi-window-example`
+in the QWidget world. The ``QRhiWidget`` subclass in this applications renders
+a single triangle, using a simple graphics pipeline with basic vertex and
+fragment shaders. Unlike the plain ``QWindow``-based application, this example
+does not need to worry about lower level details, such as setting up the window
+and the ``QRhi``, or dealing with swapchain and window events, as that is taken
+care of by the ``QWidget`` framework here. The instance of the ``QRhiWidget``
+subclass is added to a ``QVBoxLayout``. To keep the example minimal and
+compact, there are no further widgets or 3D content introduced.
+
+Once an instance of ``ExampleRhiWidget``, a ``QRhiWidget`` subclass, is added
+to a top-level widget's child hierarchy, the corresponding window automatically
+becomes a Direct 3D, Vulkan, Metal, or OpenGL-rendered window. The
+``QPainter``-rendered widget content, i.e. everything that is not a
+``QRhiWidget``, ``QOpenGLWidget``, or ``QQuickWidget``, is then uploaded to a
+texture, whereas the mentioned special widgets each render to a texture. The
+resulting set textures is composited together by the top-level widget's
+backingstore.
+
+As opposed to the C++ example, the cleanup is done by reimplementing
+``QRhiWidget.releaseResources()``, which is called from QWidget.closeEvent() of
+the top level widget to ensure a deterministic cleanup sequence.
+
+.. image:: simplerhiwidget.webp
+ :width: 400
+ :alt: Screenshot of the Simple RHI Widget example
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import numpy
+
+from PySide6.QtCore import (QFile, QIODevice)
+from PySide6.QtGui import (QColor, QMatrix4x4)
+from PySide6.QtGui import (QRhiBuffer,
+ QRhiDepthStencilClearValue,
+ QRhiShaderResourceBinding,
+ QRhiShaderStage,
+ QRhiVertexInputAttribute, QRhiVertexInputBinding,
+ QRhiVertexInputLayout, QRhiViewport,
+ QShader)
+from PySide6.QtWidgets import QRhiWidget
+from PySide6.support import VoidPtr
+
+VERTEX_DATA = numpy.array([ 0.0, 0.5, 1.0, 0.0, 0.0, # noqa E:201
+ -0.5, -0.5, 0.0, 1.0, 0.0, # noqa E:241
+ 0.5, -0.5, 0.0, 0.0, 1.0],
+ dtype=numpy.float32)
+
+
+def getShader(name):
+ f = QFile(name)
+ if f.open(QIODevice.ReadOnly):
+ return QShader.fromSerialized(f.readAll())
+ return QShader()
+
+
+class ExampleRhiWidget(QRhiWidget):
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.m_rhi = None
+ self.m_vbuf = None
+ self.m_ubuf = None
+ self.m_srb = None
+ self.m_pipeline = None
+ self.m_viewProjection = QMatrix4x4()
+ self.m_rotation = 0.0
+
+ def releaseResources(self):
+ self.m_pipeline.destroy()
+ del self.m_pipeline
+ self.m_pipeline = None
+ self.m_srb.destroy()
+ del self.m_srb
+ self.m_srb = None
+ self.m_ubuf.destroy()
+ del self.m_ubuf
+ self.m_ubuf = None
+ self.m_vbuf.destroy()
+ del self.m_vbuf
+ self.m_buf = None
+
+ def initialize(self, cb):
+ if self.m_rhi != self.rhi():
+ self.m_pipeline = None
+ self.m_rhi = self.rhi()
+
+ if not self.m_pipeline:
+ vertex_size = 4 * VERTEX_DATA.size
+ self.m_vbuf = self.m_rhi.newBuffer(QRhiBuffer.Immutable,
+ QRhiBuffer.VertexBuffer, vertex_size)
+ self.m_vbuf.create()
+
+ self.m_ubuf = self.m_rhi.newBuffer(QRhiBuffer.Dynamic,
+ QRhiBuffer.UniformBuffer, 64)
+ self.m_ubuf.create()
+
+ self.m_srb = self.m_rhi.newShaderResourceBindings()
+ bindings = [
+ QRhiShaderResourceBinding.uniformBuffer(0, QRhiShaderResourceBinding.VertexStage,
+ self.m_ubuf)
+ ]
+ self.m_srb.setBindings(bindings)
+ self.m_srb.create()
+
+ self.m_pipeline = self.m_rhi.newGraphicsPipeline()
+ stages = [
+ QRhiShaderStage(QRhiShaderStage.Vertex,
+ getShader(":/shader_assets/color.vert.qsb")),
+ QRhiShaderStage(QRhiShaderStage.Fragment,
+ getShader(":/shader_assets/color.frag.qsb"))
+ ]
+ self.m_pipeline.setShaderStages(stages)
+ inputLayout = QRhiVertexInputLayout()
+ input_bindings = [QRhiVertexInputBinding(5 * 4)] # sizeof(float)
+ inputLayout.setBindings(input_bindings)
+ attributes = [ # 4: sizeof(float)
+ QRhiVertexInputAttribute(0, 0, QRhiVertexInputAttribute.Float2, 0),
+ QRhiVertexInputAttribute(0, 1, QRhiVertexInputAttribute.Float3, 2 * 4)
+ ]
+ inputLayout.setAttributes(attributes)
+ self.m_pipeline.setVertexInputLayout(inputLayout)
+ self.m_pipeline.setShaderResourceBindings(self.m_srb)
+ self.m_pipeline.setRenderPassDescriptor(self.renderTarget().renderPassDescriptor())
+ self.m_pipeline.create()
+
+ resourceUpdates = self.m_rhi.nextResourceUpdateBatch()
+ resourceUpdates.uploadStaticBuffer(self.m_vbuf, VoidPtr(VERTEX_DATA.tobytes(),
+ vertex_size))
+ cb.resourceUpdate(resourceUpdates)
+
+ outputSize = self.renderTarget().pixelSize()
+ self.m_viewProjection = self.m_rhi.clipSpaceCorrMatrix()
+ r = float(outputSize.width()) / float(outputSize.height())
+ self.m_viewProjection.perspective(45.0, r, 0.01, 1000.0)
+ self.m_viewProjection.translate(0, 0, -4)
+
+ def render(self, cb):
+ resourceUpdates = self.m_rhi.nextResourceUpdateBatch()
+ self.m_rotation += 1.0
+ modelViewProjection = self.m_viewProjection
+ modelViewProjection.rotate(self.m_rotation, 0, 1, 0)
+ projection = numpy.array(modelViewProjection.data(),
+ dtype=numpy.float32)
+ resourceUpdates.updateDynamicBuffer(self.m_ubuf, 0, 64,
+ projection.tobytes())
+ clearColor = QColor.fromRgbF(0.4, 0.7, 0.0, 1.0)
+ cv = QRhiDepthStencilClearValue(1.0, 0)
+ cb.beginPass(self.renderTarget(), clearColor, cv, resourceUpdates)
+
+ cb.setGraphicsPipeline(self.m_pipeline)
+ outputSize = self.renderTarget().pixelSize()
+ cb.setViewport(QRhiViewport(0, 0, outputSize.width(),
+ outputSize.height()))
+ cb.setShaderResources()
+ vbufBinding = (self.m_vbuf, 0)
+ cb.setVertexInput(0, [vbufBinding])
+ cb.draw(3)
+ cb.endPass()
+
+ self.update()
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+"""PySide6 port of the Qt Simple RHI Widget Example example from Qt v6.x"""
+
+import sys
+
+from PySide6.QtWidgets import QApplication, QVBoxLayout, QWidget
+
+from examplewidget import ExampleRhiWidget
+import rc_simplerhiwidget # noqa F:401
+
+
+class Widget(QWidget):
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ layout = QVBoxLayout(self)
+ self._rhi_widget = ExampleRhiWidget(self)
+ layout.addWidget(self._rhi_widget)
+
+ def closeEvent(self, e):
+ self._rhi_widget.releaseResources()
+ e.accept()
+
+
+if __name__ == "__main__":
+ app = QApplication(sys.argv)
+
+ w = Widget()
+ w.resize(1280, 720)
+ w.show()
+ exit_code = app.exec()
+ del w
+ sys.exit(exit_code)
--- /dev/null
+# Resource object code (Python 3)
+# Created by: object code
+# Created by: The Resource Compiler for Qt version 6.7.0
+# WARNING! All changes made in this file will be lost!
+
+from PySide6 import QtCore
+
+qt_resource_data = b"\
+\x00\x00\x02\xe2\
+\x00\
+\x00\x07\xc4x\x9c\xb5T]k\xd4@\x14\x9d\xed\xa6]\
+\x9b\xd6\xda\x0f\xd07\x19\xe9\xcb\x16%\xa6\xb5\x15q\xdd\
+\xfa\xb0U)\x14Z\xdbR\x84e\x091\x9bM\x07\xb2\
+\xc9\x92\x8fE)\x05\xdf}\xf7w\xf8'\xfcO\xbe\x88\
+\xde;s\xd3\xcc\xa6[*\x88\x03\xb3\x999s\xef=\
+g\xceM\x9616\xcf\x183`\xd6`.\xb11s\
+\x98\xc7b\x16\xc2L\x00\xa9\xc3d\xbfi\xb0)\x03\xd3\
+V\xd9\x00\x82]\x16\xb0\x8e\x96j\xdc\x96\xfa\xafc\x8e\
+\x9e}M\xcd\xf7\xfa\xccz\x03U\x99\xec\x0e[#t\
+\x85\xcdH\x9d\x0b\x90\x82\xcfw\x07'\x07V\x9a\xf5\xad\
+\xed\x1d\x1b\xcf\x97\xd45\xe5\xd9=\xd6\x90\xcaq\x0e]\
+\x11!\x8e\x16-\xc2\x5c\x868\xc4\x1bX\x03V\x88?\
+$\x0e\xdc/j\xfb5\xe0\x9c\x81\xe7#\xc8\xc1\xea\xb8\
+\xbe\x0f+\x14\xcda>\x80\xf8\x06]\xc2\x90\x98\x01\x8a\
+\x95\xe1\x88\xb7h?O\x18\xc6\x9b\x14_\xa7\xf8\x05\xd2\
+lR\xfc\x02\xe9D\xec1\xec\xe7\xe4\xddp|y\xfd\
+\x9c\xcdJ\x0d\x06\xe9\xc3\x1a?\x01\x99\x85g\x9bj\xdf\
+\xa5\xfc\xf7\x80\xceI/\x14\xc64l\x99\xb0\x9a\x86\xad\
+\x10\x86\xf5\x8f@=\xea_\xa5\xfce:G\x1d\xbb\xc0\
+:Og\xbf\xa0\xc2\x0bY\x85\x19\xe4s\x8dzY#\
+\xca\xaf\xebc?IE\x1c\xf1M\xdb6G\x89\xef\x09\
+\xb9;\x17\xc1\xf9\x88\x0f\xc2\xd8\xcdZ\xd7`\x11\x01h\
+\x8e\xdd\xe4\xb3\x88\x02>\xf6\xbdg|\xecxq\x18'\
+\x08\xc7\xa2\xcf\x91\xad\xb9a^\x98\x1cF\x10:o\x13\
+7\xd8s3\xb7k\xf7x\x1b3\xb6\x9b\x94\xf1\x84o\
+Z\xf6F\xcb\xbc4\xcd\x8a\xccO\xe5+\xc7\x9cR\xe6\
+\x96\xfd\xff\xa9\xbfi\xd4\xa2\xa4\xde\x01\xea8\xcfd\x11\
+>\x80\xba\x1dE,\xa2\xdb\x95\x5c\x85\xff\x95\x08\xec\xf2\
+V\xa9\xa1\xf6#\xcd\xdcLx\xaa!\x13\xdc\xfa\x81\xae\
+ \xcd\x92\xdc\xcb\xf8\xc9\xd1\xfe\xf1\x99\xd3I\xe24u\
+\xf6\xa3Q\x9e\x15r&\xe2\xf9K~\xfa\xe6C\xe7\xf0\
+\xf0x\xcf\x06)\xd3\xd3\x0f\xf3\xac\x92\xaf\x09\x81\x0a'\
+g\xce\xa9\x9b\x04~F%\xa4\x07\x18\xe0\xdch\x84\xaa\
+2a\xc5@yq\x9dX\xd9y\xed>\x1c\x0c\x08|\
+G\xe0\xba\xa0(n\xd5\xd6\x0f\xad+o\x0a\x15\xa4K\
+\x01S\x08Ur,7*HG\xac\x89\x8b\x94\x0d\xc1\
+\xb8\xc4\xcf\xf2$\xaa\x14\xb8\xd4\x1bl\xd0\xdf@\xd1\xe0\
+\xfdu\x11ya\xde\xf7\xf9\xab\xa1\x9f\xb9\xa1\x03\x7f\x9d\
+\xa1\xf8\xb8k\x96x*\x86\xfd\xa7\xf8c\x9d\xef\x9af\
+\x9e\xe2\x07\x10\xb9C?\x1d\xb9\x9e\xcfeV\xd98\xa4\
+\xb1\x91\xfa\xa6~u\xbb\xd2\x8d\xa6\xbd\xd1\xebM\xb4\x5c\
+e\x8ah\xfa\x8b\xd2\xed\xe6\xa9\x9f4\xc3\xd8\x8b\xcaL\
+\xac:\xf4#\x8dU\xad\x9aE-\xf8\xc7\x80\xcc\xa2\x19\
+\xbd^\xd1\xa82\x1eg\x9b_\x5c*\xff\xe2\xaa\xbd\xea\
+=\x11\x91U\xf9j4\xb3c\xe51\x9a<+\xeb\xd2\
+gT\xf9\x94\xe4\xa8\xb8_\xb6\xe1\xa6\xb3?Y\xbd\xa1\
+\xc8\
+\x00\x00\x04C\
+\x00\
+\x00\x0c\xf4x\x9c\xddW[o\x1bE\x14\x1e_\x92\x92\
+-\xb9\xb4i\x93\x16\xda2\xa9%dCe\x9c4A\
+\xa8&\x11\xa8 Z\xa9R\xaa\x06UHV\xb4Z\xaf\
+\xd7\xe9\xc2^\xac\xddY7\xa8\xf2\x8f\xe0\x85\xbf\xc3o\
+\xe0o\xf0\xc8\x13/U9g\xe6\xcc\xee\xec\xda\xa1*\
+\x0f<0\xd2d\xe7\x5c\xe6|\xe7|sf\x920\xc6\
+V\x98\x1au\x98\x16sY\xcc\x02\x98\x09H\x0d\x98\xb5\
+74\xd8\x82\xb1\xc1&\xe0\x9a2\x9f\x09\x981\x8b@\
+\xd7D\xc3?m\xaa\xc1\x5ccSf\xcfa\xbdu\xdb\
+2\x1b\xb2\x8c\x8d\xe5\xcaf{\xec\x00V_\xcd\xf9\x84\
+\x10|\x02\xab\xcb\xa45=6\x16\x85~\x97Q\xa3o\
+\x93\xbe\xcb\xf4\x1d\x15.\x8d\x17\x8dz\xeb\x12\xbaZ\xec\
+=\xd6\x22\xed\x15 \xb8&\xb3Z\x96\xdf\xef\x9e\x9c<\
+\xe9\xa6b\xd4\xdd?\xe8\xa1}MQ m\xebt(\
+\x08\x12:>\xd2*5X\xd1&\xcc;\xb8\x1f\xac+\
+\xb4f$_&\x19c<bK\x80\xa9\x86fB\xeb\
+j\xa43\xfd\xea\xa4k\xc8X\x8d\x5c\xf7\x08\xe2n\xd3\
+\xfe%\xf2\xd7r\xcb\x88\xabuP\xb8d\x19clS\
+\x0c\xcc\xed&|\xef\x1a\xb9\xa2\xbcc\xc8w\x8cZ6\
+aW]\xda\x1b2\x1f\x5co\xc1\x0a\xc9\xe60\xb7\xc1\
+\xff\x12\x91\xdf\x90\xba&0\xad\xd6\xa8\xef\x93\xbcb\xd8\
+-\xaaW\xdb-\x83\x03\x8c\xb7J\xf1\x90\xf3\xeb\xf0s\
+\x8d\xb0p|J\xf2:\xf9\xdf\x02y\x83\xfc\xd7e\xde\
+\x8aC\x1dc\x830\xaf\x12\xfe\x15\xc2\xbcJ\xe7\xd7 \
+\x8ck\x84Q#\x8ckR\xaf\xc6\x0d\x90\xb7(fS\
+b(>\xb7(\xf6\x0d\xe2e\x9bb\xa3|\x93th\
+\xff \xe7M\xc9\xb7\x09g\x95\xfco\xb3\xa2W\xd0\xbe\
+Cy\xa1\xfds\xa8\xa7\xce\x8a&G\xfd_\xa0\xc1\xf3\
+?$\xee\xdf'\xfe\x8e\xc0\xbaB\xf2\xd7\xe0\x81\xb8\x1f\
+R.\xd7\xc9\x7fKr\xa6\xf4\x87\xc4\xf5G\x84\xff\x0b\
+\xecY%\x1en\x91\x1e\xe3\xecP\xbflR\x1c\xc4\xb9\
+K~\xaf!\xeb/\xd4Ul\xd2\xfd@a\xc4\x8a\xfb\
+\xf9{k\xea%\xa9\x1fG|\xb7\xd7\xb3\xacT$\x99\
++\xf80\x1b[\xaf,\x0e#t\xc4>\x0f\xa7\x93\xbe\
+5\xeb[V\x16\xf9\xe38\x09\xd1\xce\xed\xbd\x03\xd0L\
+\x9d\xe4g?:\xe3S\xcf\xbd\xcf\xa7\xb6\x1b\x07q\xd2\
+\xb7\x1c!\x12\x7f\x98\x09O\xe9\x17i\xf7\xf9$N}\
+\x01\xc8\x18%\xf6G\x1c3lw\x08\x97B\xf1C\xbd\
+\x19\x95g\x81\xfd\x946\x81\x01\x12\xe8Bf\xfc\x13#\
+\xd2\xcc\xb2*\xc5\x9e\xb3b\x18\xc5\xee\xfd/\x8b\xfd\xd5\
+(\xf6\xb7\xa2\xd8\x83\x7fSl\x9c\x89J\xa1~T\xaa\
+P\x89\xffEix\xc7\xf6\x8a\xca\xea\x7f\xb8\x90\xe6\xd8\
+Kd\xb6\x0fx\xe2\x9d\xf9\xa9\xf0\x92\xf6\xb0\xa7a\x93\
+\xf8\xa5\x1d:?\x02\xf08\x88\xa1\xd0\xf3}\x84\xb1\x11\
+\xe6\x01\x9f8\xeeO\xf1x\x9cz\xa2\xed\xf6:\xaa~\
+\xe0\xc7\x11\xbeK\xeef\x86\xfd\x92\xc9\xa0\xa3\xac^\xa0\
+,QC\xfc\x9f<}\xfc\xec\xb9\xfd0\x89\xd3\xd4~\
+\x1cM2A\x09W6@\x96\xdf\x7f\xfb\xc3\xc3\xe3\xe3\
+g\xdf\xf4\xfa\x85\x03\xc1\x18\xd6]\x95\xfe\x82\xe8\xc7\x99\
+\xa8\x84\xcfs\xbf z\xa9l\xf09yn\x900\xd3\
+\xc7\x0b]%\xecw=\xe30\x0b\xda\xba\xb6{\xfa(\
+:\xf2\x9c\xe7sV\x1d4\xc7\x14\x07n\xcf<\xdb\xc7\
+\xb5\x86\xd6\xc0\x86\xa9k$1)20=\x8ac\x91\
+\xe9\x17\x05)\xc5\x82\x8c\xd4\xeeX\x0a\xca\xc9\xd4t\xcb\
+\xc5\x96\x9ag\xce\xb7`+\xef$\xd9\xb1\x9e\xc8\x92\xa8\
+\x0243oA\x93~{\xd0\xa8\xfd\xd9\xf2#7\xc8\
+F\x1e\xff2\xf4\x84\x13\xd8\xf0WR\xe0\x0f\x8f\xacB\
+\x9f\xfa\xe1\xe83\xfc\xd1}q\x04w<\xc5\xc7+r\
+B/\x85+\xe0q\xb9\xab\xbf\xe0i\xc8\xefL\xfe<\
+\x90\x0bf\xd2\xc3\xec\x16\xb7\xd5`\x90\xa5p\x0d\x83\xd8\
+\x8dz\x9d\xd3\xd3\x0b[k0\xd0'\x80Ns\xf1\xfd\
+\xe8\x82K1\x18\xe4\xafj\xbb\x02\xa0o\x86\xe9\xb2\xdb\
+\xd1\xe1\xf1\x88\xbd\xf3\x22}\xb5jk8\xeecl\xdd\
+!\xa7\xa7\xf7 X\x04b$I\xf9\x18\xfb\x15\xec\xea\
+\xcd\x91\xc0\x9d\xfc\x0d\xd5\x01q\x1e\xf2W3\x95R\x5c\
+:g?2{2\x9e\xeb\x97\xe2\x01\x04\xcfroR\
+W\xc4\xaa\x19\xb0\x1b\x96$&=\x8a\x95\x871\xff\xad\
+\xce\xe8\xbf\x83J\xcb\xcc\xd9\xb5\xae\xea'\xc7\xdf%\xe1\
+\xb4\xa2\
+"
+
+qt_resource_name = b"\
+\x00\x0d\
+\x06E\xc5\xd3\
+\x00s\
+\x00h\x00a\x00d\x00e\x00r\x00_\x00a\x00s\x00s\x00e\x00t\x00s\
+\x00\x0e\
+\x04\x16\xeb\xb2\
+\x00c\
+\x00o\x00l\x00o\x00r\x00.\x00f\x00r\x00a\x00g\x00.\x00q\x00s\x00b\
+\x00\x0e\
+\x00\xfb\xe9\x92\
+\x00c\
+\x00o\x00l\x00o\x00r\x00.\x00v\x00e\x00r\x00t\x00.\x00q\x00s\x00b\
+"
+
+qt_resource_struct = b"\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x02\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00B\x00\x00\x00\x00\x00\x01\x00\x00\x02\xe6\
+\x00\x00\x01\x8a!\x0c\xa5\xeb\
+\x00\x00\x00 \x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
+\x00\x00\x01\x8a!\x0c\xa5\xeb\
+"
+
+def qInitResources():
+ QtCore.qRegisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+def qCleanupResources():
+ QtCore.qUnregisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+qInitResources()
--- /dev/null
+#version 440
+
+layout(location = 0) in vec3 v_color;
+
+layout(location = 0) out vec4 fragColor;
+
+void main()
+{
+ fragColor = vec4(v_color, 1.0);
+}
--- /dev/null
+#version 440
+
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec3 color;
+
+layout(location = 0) out vec3 v_color;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 mvp;
+};
+
+void main()
+{
+ v_color = color;
+ gl_Position = mvp * position;
+}
--- /dev/null
+{
+ "files": ["main.py","examplewidget.py", "simplerhiwidget.qrc",
+ "shaders/color.frag", "shaders/color.vert"]
+}
--- /dev/null
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/">
+ <file>shader_assets/color.vert.qsb</file>
+ <file>shader_assets/color.frag.qsb</file>
+</qresource>
+</RCC>
tb = self.addToolBar("File self.actions")
menu = self.menuBar().addMenu("&File")
- icon = QIcon.fromTheme("document-new", QIcon(RSRC_PATH + "/filenew.png"))
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentNew,
+ QIcon(RSRC_PATH + "/filenew.png"))
a = menu.addAction(icon, "&New", self.file_new)
tb.addAction(a)
a.setPriority(QAction.LowPriority)
a.setShortcut(QKeySequence.New)
- icon = QIcon.fromTheme("document-open",
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentOpen,
QIcon(RSRC_PATH + "/fileopen.png"))
a = menu.addAction(icon, "&Open...", self.file_open)
a.setShortcut(QKeySequence.Open)
menu.addSeparator()
- icon = QIcon.fromTheme("document-save",
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentSave,
QIcon(RSRC_PATH + "/filesave.png"))
self._action_save = menu.addAction(icon, "&Save", self.file_save)
self._action_save.setShortcut(QKeySequence.Save)
a.setPriority(QAction.LowPriority)
menu.addSeparator()
- icon = QIcon.fromTheme("document-print",
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentPrint,
QIcon(RSRC_PATH + "/fileprint.png"))
a = menu.addAction(icon, "&Print...", self.file_print)
a.setPriority(QAction.LowPriority)
tb = self.addToolBar("Edit self.actions")
menu = self.menuBar().addMenu("&Edit")
- icon = QIcon.fromTheme("edit-undo",
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.EditUndo,
QIcon(RSRC_PATH + "/editundo.png"))
self._action_undo = menu.addAction(icon, "&Undo", self._text_edit.undo)
self._action_undo.setShortcut(QKeySequence.Undo)
tb.addAction(self._action_undo)
- icon = QIcon.fromTheme("edit-redo", QIcon(RSRC_PATH + "/editredo.png"))
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.EditRedo,
+ QIcon(RSRC_PATH + "/editredo.png"))
self._action_redo = menu.addAction(icon, "&Redo", self._text_edit.redo)
self._action_redo.setPriority(QAction.LowPriority)
self._action_redo.setShortcut(QKeySequence.Redo)
tb.addAction(self._action_redo)
menu.addSeparator()
- icon = QIcon.fromTheme("edit-cut", QIcon(RSRC_PATH + "/editcut.png"))
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.EditCut,
+ QIcon(RSRC_PATH + "/editcut.png"))
self._action_cut = menu.addAction(icon, "Cu&t", self._text_edit.cut)
self._action_cut.setPriority(QAction.LowPriority)
self._action_cut.setShortcut(QKeySequence.Cut)
tb.addAction(self._action_cut)
- icon = QIcon.fromTheme("edit-copy", QIcon(RSRC_PATH + "/editcopy.png"))
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.EditCopy,
+ QIcon(RSRC_PATH + "/editcopy.png"))
self._action_copy = menu.addAction(icon, "&Copy", self._text_edit.copy)
self._action_copy.setPriority(QAction.LowPriority)
self._action_copy.setShortcut(QKeySequence.Copy)
tb.addAction(self._action_copy)
- icon = QIcon.fromTheme("edit-paste", QIcon(RSRC_PATH + "/editpaste.png"))
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.EditPaste,
+ QIcon(RSRC_PATH + "/editpaste.png"))
self._action_paste = menu.addAction(icon, "&Paste", self._text_edit.paste)
self._action_paste.setPriority(QAction.LowPriority)
self._action_paste.setShortcut(QKeySequence.Paste)
tb = self.addToolBar("Format self.actions")
menu = self.menuBar().addMenu("F&ormat")
- icon = QIcon.fromTheme("format-text-bold",
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatTextBold,
QIcon(RSRC_PATH + "/textbold.png"))
self._action_text_bold = menu.addAction(icon, "&Bold", self.text_bold)
self._action_text_bold.setShortcut(Qt.CTRL | Qt.Key_B)
tb.addAction(self._action_text_bold)
self._action_text_bold.setCheckable(True)
- icon = QIcon.fromTheme("format-text-italic",
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatTextItalic,
QIcon(RSRC_PATH + "/textitalic.png"))
self._action_text_italic = menu.addAction(icon, "&Italic", self.text_italic)
self._action_text_italic.setPriority(QAction.LowPriority)
tb.addAction(self._action_text_italic)
self._action_text_italic.setCheckable(True)
- icon = QIcon.fromTheme("format-text-underline",
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatTextUnderline,
QIcon(RSRC_PATH + "/textunder.png"))
self._action_text_underline = menu.addAction(icon, "&Underline",
self.text_underline)
menu.addSeparator()
- icon = QIcon.fromTheme("format-justify-left",
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatJustifyLeft,
QIcon(RSRC_PATH + "/textleft.png"))
self._action_align_left = QAction(icon, "&Left", self)
self._action_align_left.setShortcut(Qt.CTRL | Qt.Key_L)
self._action_align_left.setCheckable(True)
self._action_align_left.setPriority(QAction.LowPriority)
- icon = QIcon.fromTheme("format-justify-center",
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatJustifyCenter,
QIcon(RSRC_PATH + "/textcenter.png"))
self._action_align_center = QAction(icon, "C&enter", self)
self._action_align_center.setShortcut(Qt.CTRL | Qt.Key_E)
self._action_align_center.setCheckable(True)
self._action_align_center.setPriority(QAction.LowPriority)
- icon = QIcon.fromTheme("format-justify-right",
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatJustifyRight,
QIcon(RSRC_PATH + "/textright.png"))
self._action_align_right = QAction(icon, "&Right", self)
self._action_align_right.setShortcut(Qt.CTRL | Qt.Key_R)
self._action_align_right.setCheckable(True)
self._action_align_right.setPriority(QAction.LowPriority)
- icon = QIcon.fromTheme("format-justify-fill",
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatJustifyFill,
QIcon(RSRC_PATH + "/textjustify.png"))
self._action_align_justify = QAction(icon, "&Justify", self)
self._action_align_justify.setShortcut(Qt.CTRL | Qt.Key_J)
self._action_align_justify.setCheckable(True)
self._action_align_justify.setPriority(QAction.LowPriority)
- icon = QIcon.fromTheme("format-indent-more",
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatIndentMore,
QIcon(RSRC_PATH + "/format-indent-more.png"))
self._action_indent_more = menu.addAction(icon, "&Indent", self.indent)
self._action_indent_more.setShortcut(Qt.CTRL | Qt.Key_BracketRight)
self._action_indent_more.setPriority(QAction.LowPriority)
- icon = QIcon.fromTheme("format-indent-less",
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatIndentLess,
QIcon(RSRC_PATH + "/format-indent-less.png"))
self._action_indent_less = menu.addAction(icon, "&Unindent",
self.unindent)
sphinx-design==0.5.0
sphinx-copybutton==0.5.2
sphinx-tags==0.3.1
+sphinx-toolbox
myst-parser==2.0.0
# FIXME: Using fork in order to enable the 'collapse_navbar=True'
# option for the sphinx-theme. Upstream proposal:
# Build dependencies
-setuptools==69.0.3
+setuptools==69.1.1
packaging==23.2
build==1.0.3
wheel==0.42.0
# For examples
PyOpenGL
-
-# For tests
-pyinstaller==3.6; platform_machine != 'aarch64'
"${TOOLS_PATH}/lupdate${CMAKE_EXECUTABLE_SUFFIX}"
"${TOOLS_PATH}/qmllint${CMAKE_EXECUTABLE_SUFFIX}"
"${TOOLS_PATH}/qmlformat${CMAKE_EXECUTABLE_SUFFIX}"
- "${TOOLS_PATH}/qmlls${CMAKE_EXECUTABLE_SUFFIX}")
+ "${TOOLS_PATH}/qmlls${CMAKE_EXECUTABLE_SUFFIX}"
+ "${TOOLS_PATH}/qsb${CMAKE_EXECUTABLE_SUFFIX}"
+ "${TOOLS_PATH}/balsam${CMAKE_EXECUTABLE_SUFFIX}"
+ "${TOOLS_PATH}/balsamui${CMAKE_EXECUTABLE_SUFFIX}")
if (APPLE)
list(APPEND directories "${TOOLS_PATH}/Assistant.app"
from pathlib import Path
from textwrap import dedent
-from deploy_lib import (setup_python, create_config_file, cleanup, install_python_dependencies,
- config_option_exists, MAJOR_VERSION)
+from deploy_lib import (create_config_file, cleanup, config_option_exists, PythonExecutable,
+ MAJOR_VERSION, HELP_EXTRA_IGNORE_DIRS, HELP_EXTRA_MODULES)
from deploy_lib.android import AndroidData, AndroidConfig
from deploy_lib.android.buildozer import Buildozer
Note: This file is used by both pyside6-deploy and pyside6-android-deploy
"""
-HELP_EXTRA_IGNORE_DIRS = dedent("""
- Comma separated directory names inside the project dir. These
- directories will be skipped when searching for python files
- relevant to the project.
-
- Example usage: --extra-ignore-dirs=doc,translations
- """)
-
-HELP_EXTRA_MODULES = dedent("""
- Comma separated list of Qt modules to be added to the application,
- in case they are not found automatically.
-
- This occurs when you have 'import PySide6' in your code instead
- 'from PySide6 import <module>'. The module name is specified
- with either omitting the prefix of Qt or with it.
-
- Example usage 1: --extra-modules=Network,Svg
- Example usage 2: --extra-modules=QtNetwork,QtSvg
- """)
-
def main(name: str = None, pyside_wheel: Path = None, shiboken_wheel: Path = None,
ndk_path: Path = None, sdk_path: Path = None, config_file: Path = None, init: bool = False,
android_data = AndroidData(wheel_pyside=pyside_wheel, wheel_shiboken=shiboken_wheel,
ndk_path=ndk_path, sdk_path=sdk_path)
- python = setup_python(dry_run=dry_run, force=force, init=init)
+ python = PythonExecutable(dry_run=dry_run, init=init, force=force)
config_file_exists = config_file and Path(config_file).exists()
cleanup(config=config, is_android=True)
- install_python_dependencies(config=config, python=python, init=init,
- packages="android_packages", is_android=True)
+ python.install_dependencies(config=config, packages="android_packages", is_android=True)
# set application name
if name:
config.title = name
try:
- config.modules += extra_modules
+ config.modules += list(set(extra_modules).difference(set(config.modules)))
# this cannot be done when config file is initialized because cleanup() removes it
# so this can only be done after the cleanup()
config.find_and_set_jars_dir()
+ config.verify_and_set_recipe_dir()
# TODO: include qml files from pysidedeploy.spec rather than from extensions
# buildozer currently includes all the files with .qml extension
"deploy_lib/android/recipes/PySide6/__init__.tmpl.py",
"deploy_lib/android/recipes/shiboken6/__init__.tmpl.py",
"deploy_lib/android/__init__.py", "deploy_lib/android/android_helper.py",
- "deploy_lib/android/buildozer.py"
+ "deploy_lib/android/buildozer.py", "deploy_lib/dependency_util.py"
]
}
set(android_main_srcs "${QT6_INSTALL_PREFIX}/src/android/java/src/org/qtproject/qt/android/bindings")
set(java_sources
${android_main_srcs}/QtActivity.java
- ${android_main_srcs}/QtActivityLoader.java
${android_main_srcs}/QtApplication.java
- ${android_main_srcs}/QtLoader.java
${android_main_srcs}/QtService.java
- ${android_main_srcs}/QtServiceLoader.java
)
# set android.jar from the sdk, for compiling the java files into .jar
set(sdk_jar_location "${ANDROID_SDK_ROOT}/platforms/${ANDROID_PLATFORM}/android.jar")
from pathlib import Path
from textwrap import dedent
-from deploy_lib import (MAJOR_VERSION, Config, cleanup, config_option_exists,
- finalize, create_config_file, install_python_dependencies,
- setup_python)
+from deploy_lib import (MAJOR_VERSION, DesktopConfig, cleanup, config_option_exists,
+ finalize, create_config_file, PythonExecutable, Nuitka,
+ HELP_EXTRA_MODULES, HELP_EXTRA_IGNORE_DIRS)
+
+
+TOOL_DESCRIPTION = dedent(f"""
+ This tool deploys PySide{MAJOR_VERSION} to desktop (Windows, Linux,
+ macOS) platforms. The following types of executables are produced as per
+ the platform:
+
+ Windows = .exe
+ macOS = .app
+ Linux = .bin
+ """)
def main(main_file: Path = None, name: str = None, config_file: Path = None, init: bool = False,
loglevel=logging.WARNING, dry_run: bool = False, keep_deployment_files: bool = False,
- force: bool = False):
+ force: bool = False, extra_ignore_dirs: str = None, extra_modules_grouped: str = None):
logging.basicConfig(level=loglevel)
if config_file and not config_file.exists() and not main_file.exists():
config = None
logging.info("[DEPLOY] Start")
- python = setup_python(dry_run=dry_run, force=force, init=init)
+ if extra_ignore_dirs:
+ extra_ignore_dirs = extra_ignore_dirs.split(",")
+
+ extra_modules = []
+ if extra_modules_grouped:
+ tmp_extra_modules = extra_modules_grouped.split(",")
+ for extra_module in tmp_extra_modules:
+ if extra_module.startswith("Qt"):
+ extra_modules.append(extra_module[2:])
+ else:
+ extra_modules.append(extra_module)
+
+ python = PythonExecutable(dry_run=dry_run, init=init, force=force)
config_file_exists = config_file and Path(config_file).exists()
if config_file_exists:
config_file = create_config_file(dry_run=dry_run, config_file=config_file,
main_file=main_file)
- config = Config(config_file=config_file, source_file=main_file, python_exe=python.exe,
- dry_run=dry_run, existing_config_file=config_file_exists)
+ config = DesktopConfig(config_file=config_file, source_file=main_file, python_exe=python.exe,
+ dry_run=dry_run, existing_config_file=config_file_exists,
+ extra_ignore_dirs=extra_ignore_dirs)
# set application name
if name:
cleanup(config=config)
- install_python_dependencies(config=config, python=python, init=init,
- packages="packages")
+ python.install_dependencies(config=config, packages="packages")
# required by Nuitka for pyenv Python
add_arg = " --static-libpython=no"
if python.is_pyenv_python() and add_arg not in config.extra_args:
config.extra_args += add_arg
+ config.modules += list(set(extra_modules).difference(set(config.modules)))
+
# writing config file
# in the case of --dry-run, we use default.spec as reference. Do not save the changes
# for --dry-run
if not dry_run:
config.update_config()
+ if config.qml_files:
+ logging.info(f"[DEPLOY] Included QML files: {config.qml_files}")
+
if init:
# config file created above. Exiting.
logging.info(f"[DEPLOY]: Config file {config.config_file} created")
if not dry_run:
logging.info("[DEPLOY] Deploying application")
- command_str = python.create_executable(source_file=config.source_file,
+ nuitka = Nuitka(nuitka=[python.exe, "-m", "nuitka"])
+ command_str = nuitka.create_executable(source_file=config.source_file,
extra_args=config.extra_args,
- config=config)
+ qml_files=config.qml_files,
+ qt_plugins=config.qt_plugins,
+ excluded_qml_plugins=config.excluded_qml_plugins,
+ icon=config.icon,
+ dry_run=dry_run,
+ permissions=config.permissions)
except Exception:
print(f"[DEPLOY] Exception occurred: {traceback.format_exc()}")
finally:
if __name__ == "__main__":
- parser = argparse.ArgumentParser(
- description=(f"This tool deploys PySide{MAJOR_VERSION} to desktop (Windows, Linux, macOS)"
- "platforms"),
- formatter_class=argparse.RawTextHelpFormatter,
- )
+ parser = argparse.ArgumentParser(description=TOOL_DESCRIPTION)
parser.add_argument("-c", "--config-file", type=lambda p: Path(p).absolute(),
default=(Path.cwd() / "pysidedeploy.spec"),
parser.add_argument("--name", type=str, help="Application name")
+ parser.add_argument("--extra-ignore-dirs", type=str, help=HELP_EXTRA_IGNORE_DIRS)
+
+ parser.add_argument("--extra-modules", type=str, help=HELP_EXTRA_MODULES)
+
args = parser.parse_args()
main(args.main_file, args.name, args.config_file, args.init, args.loglevel, args.dry_run,
- args.keep_deployment_files, args.force)
+ args.keep_deployment_files, args.force, args.extra_ignore_dirs, args.extra_modules)
"files": ["deploy.py", "deploy_lib/__init__.py", "deploy_lib/commands.py", "deploy_lib/config.py",
"deploy_lib/default.spec", "deploy_lib/nuitka_helper.py", "deploy_lib/pyside_icon.ico",
"deploy_lib/pyside_icon.icns","deploy_lib/pyside_icon.jpg",
- "deploy_lib/python_helper.py", "deploy_lib/deploy_util.py"
+ "deploy_lib/python_helper.py", "deploy_lib/deploy_util.py",
+ "deploy_lib/dependency_util.py"
]
}
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
import sys
from pathlib import Path
+from textwrap import dedent
MAJOR_VERSION = 6
EXE_FORMAT = ".exe"
elif sys.platform == "darwin":
IMAGE_FORMAT = ".icns"
- EXE_FORMAT = ".bin"
+ EXE_FORMAT = ".app"
else:
IMAGE_FORMAT = ".jpg"
EXE_FORMAT = ".bin"
DEFAULT_APP_ICON = str((Path(__file__).parent / f"pyside_icon{IMAGE_FORMAT}").resolve())
+IMPORT_WARNING_PYSIDE = (f"[DEPLOY] Found 'import PySide6' in file {0}"
+ ". Use 'from PySide6 import <module>' or pass the module"
+ " needed using --extra-modules command line argument")
+HELP_EXTRA_IGNORE_DIRS = dedent("""
+ Comma-separated directory names inside the project dir. These
+ directories will be skipped when searching for Python files
+ relevant to the project.
+
+ Example usage: --extra-ignore-dirs=doc,translations
+ """)
+
+HELP_EXTRA_MODULES = dedent("""
+ Comma-separated list of Qt modules to be added to the application,
+ in case they are not found automatically.
+
+ This occurs when you have 'import PySide6' in your code instead
+ 'from PySide6 import <module>'. The module name is specified
+ by either omitting the prefix of Qt or including it.
+
+ Example usage 1: --extra-modules=Network,Svg
+ Example usage 2: --extra-modules=QtNetwork,QtSvg
+ """)
+
+
+def get_all_pyside_modules():
+ """
+ Returns all the modules installed with PySide6
+ """
+ import PySide6
+ # They all start with `Qt` as the prefix. Removing this prefix and getting the actual
+ # module name
+ return [module[2:] for module in PySide6.__all__]
+
-from .commands import run_command
+from .commands import run_command, run_qmlimportscanner
+from .dependency_util import find_pyside_modules, find_permission_categories, QtDependencyReader
from .nuitka_helper import Nuitka
-from .python_helper import PythonExecutable, find_pyside_modules
-from .config import BaseConfig, Config
-from .deploy_util import (cleanup, finalize, create_config_file, setup_python,
- install_python_dependencies, config_option_exists)
+from .config import BaseConfig, Config, DesktopConfig
+from .python_helper import PythonExecutable
+from .deploy_util import cleanup, finalize, create_config_file, config_option_exists
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-from .android_helper import (create_recipe, extract_and_copy_jar,
- get_wheel_android_arch, AndroidData, get_llvm_readobj,
- find_lib_dependencies, find_qtlibs_in_wheel)
+# maps instruction set to Android platform names
+platform_map = {"aarch64": "arm64-v8a",
+ "armv7a": "armeabi-v7a",
+ "i686": "x86",
+ "x86_64": "x86_64",
+ "arm64-v8a": "arm64-v8a",
+ "armeabi-v7a": "armeabi-v7a",
+ "x86": "x86"}
+
+from .android_helper import (create_recipe, extract_and_copy_jar, get_wheel_android_arch,
+ AndroidData, get_llvm_readobj, find_lib_dependencies,
+ find_qtlibs_in_wheel)
from .android_config import AndroidConfig
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+import re
+import tempfile
import logging
+import zipfile
+import xml.etree.ElementTree as ET
from typing import List
from pathlib import Path
+from pkginfo import Wheel
-from . import extract_and_copy_jar, get_wheel_android_arch
-from .. import Config, find_pyside_modules
+from . import (extract_and_copy_jar, get_wheel_android_arch, find_lib_dependencies,
+ get_llvm_readobj, find_qtlibs_in_wheel, platform_map, create_recipe)
+from .. import (Config, find_pyside_modules, get_all_pyside_modules, MAJOR_VERSION)
-ANDROID_NDK_VERSION = "25c"
+ANDROID_NDK_VERSION = "26b"
ANDROID_DEPLOY_CACHE = Path.home() / ".pyside6_android_deploy"
if jars_dir_temp and Path(jars_dir_temp).resolve().exists():
self.jars_dir = Path(jars_dir_temp).resolve()
- self._modules = []
- if self.get_value("buildozer", "modules"):
- self.modules = self.get_value("buildozer", "modules").split(",")
- else:
- self._find_and_set_pysidemodules()
- self._find_and_set_qtquick_modules()
-
self._arch = None
if self.get_value("buildozer", "arch"):
self.arch = self.get_value("buildozer", "arch")
else:
self._find_and_set_arch()
+ # maps to correct platform name incase the instruction set was specified
+ self._arch = platform_map[self.arch]
+
+ self._mode = self.get_value("buildozer", "mode")
+
+ self.qt_libs_path: zipfile.Path = find_qtlibs_in_wheel(wheel_pyside=self.wheel_pyside)
+ logging.info(f"[DEPLOY] Qt libs path inside wheel: {str(self.qt_libs_path)}")
+
+ if self.get_value("qt", "modules"):
+ self.modules = self.get_value("qt", "modules").split(",")
+ else:
+ self._find_and_set_pysidemodules()
+ self._find_and_set_qtquick_modules()
+ self.modules += self._find_dependent_qt_modules()
+ # remove duplicates
+ self.modules = list(set(self.modules))
+
+ # gets the xml dependency files from Qt installation path
+ self._dependency_files = []
+ self._find_and_set_dependency_files()
+
+ dependent_plugins = []
self._local_libs = []
if self.get_value("buildozer", "local_libs"):
- self.local_libs = self.get_value("buildozer", "local_libs").split(",")
+ self._local_libs = self.get_value("buildozer", "local_libs").split(",")
+ else:
+ # the local_libs can also store dependent plugins
+ local_libs, dependent_plugins = self._find_local_libs()
+ self.local_libs = list(set(local_libs))
self._qt_plugins = []
if self.get_value("android", "plugins"):
self._qt_plugins = self.get_value("android", "plugins").split(",")
+ elif dependent_plugins:
+ self._find_plugin_dependencies(dependent_plugins)
+ self.qt_plugins = list(set(dependent_plugins))
- self._mode = self.get_value("buildozer", "mode")
+ recipe_dir_temp = self.get_value("buildozer", "recipe_dir")
+ if recipe_dir_temp:
+ self.recipe_dir = Path(recipe_dir_temp)
@property
def qt_plugins(self):
@modules.setter
def modules(self, modules):
self._modules = modules
- self.set_value("buildozer", "modules", ",".join(modules))
+ self.set_value("qt", "modules", ",".join(modules))
@property
def local_libs(self):
if self._wheel_shiboken:
self.set_value("android", "wheel_shiboken", str(self._wheel_shiboken))
+ @property
+ def dependency_files(self):
+ return self._dependency_files
+
+ @dependency_files.setter
+ def dependency_files(self, dependency_files):
+ self._dependency_files = dependency_files
+
def _find_and_set_pysidemodules(self):
self.modules = find_pyside_modules(project_dir=self.project_dir,
extra_ignore_dirs=self.extra_ignore_dirs,
raise RuntimeError("[DEPLOY] PySide wheel corrupted. Wheel name should end with"
"platform name")
- def _find_and_set_qtquick_modules(self):
- """Identify if QtQuick is used in QML files and add them as dependency
+ def _find_dependent_qt_modules(self):
"""
- extra_modules = []
-
- if "QtQuick" in self.qml_modules:
- extra_modules.append("Quick")
-
- if "QtQuick.Controls" in self.qml_modules:
- extra_modules.append("QuickControls2")
-
- self.modules += extra_modules
+ Given pysidedeploy_config.modules, find all the other dependent Qt modules. This is
+ done by using llvm-readobj (readelf) to find the dependent libraries from the module
+ library.
+ """
+ dependent_modules = set()
+ all_dependencies = set()
+ lib_pattern = re.compile(f"libQt6(?P<mod_name>.*)_{self.arch}")
+
+ llvm_readobj = get_llvm_readobj(self.ndk_path)
+ if not llvm_readobj.exists():
+ raise FileNotFoundError(f"[DEPLOY] {llvm_readobj} does not exist."
+ "Finding Qt dependencies failed")
+
+ archive = zipfile.ZipFile(self.wheel_pyside)
+ lib_path_suffix = Path(str(self.qt_libs_path)).relative_to(self.wheel_pyside)
+
+ with tempfile.TemporaryDirectory() as tmpdir:
+ archive.extractall(tmpdir)
+ qt_libs_tmpdir = Path(tmpdir) / lib_path_suffix
+ # find the lib folder where Qt libraries are stored
+ for module_name in sorted(self.modules):
+ qt_module_path = qt_libs_tmpdir / f"libQt6{module_name}_{self.arch}.so"
+ if not qt_module_path.exists():
+ raise FileNotFoundError(f"[DEPLOY] libQt6{module_name}_{self.arch}.so not found"
+ " inside the wheel")
+ find_lib_dependencies(llvm_readobj=llvm_readobj, lib_path=qt_module_path,
+ dry_run=self.dry_run,
+ used_dependencies=all_dependencies)
+
+ for dependency in all_dependencies:
+ match = lib_pattern.search(dependency)
+ if match:
+ module = match.group("mod_name")
+ if module not in self.modules:
+ dependent_modules.add(module)
+
+ # check if the PySide6 binary for the Qt module actually exists
+ # eg: libQt6QmlModels.so exists and it includes QML types. Hence, it makes no
+ dependent_modules = [module for module in dependent_modules if module in
+ get_all_pyside_modules()]
+ dependent_modules_str = ",".join(dependent_modules)
+ logging.info("[DEPLOY] The following extra dependencies were found:"
+ f" {dependent_modules_str}")
+
+ return dependent_modules
+
+ def _find_and_set_dependency_files(self) -> List[zipfile.Path]:
+ """
+ Based on `modules`, returns the Qt6{module}_{arch}-android-dependencies.xml file, which
+ contains the various dependencies of the module, like permissions, plugins etc
+ """
+ needed_dependency_files = [(f"Qt{MAJOR_VERSION}{module}_{self.arch}"
+ "-android-dependencies.xml") for module in self.modules]
+
+ for dependency_file_name in needed_dependency_files:
+ dependency_file = self.qt_libs_path / dependency_file_name
+ if dependency_file.exists():
+ self._dependency_files.append(dependency_file)
+
+ logging.info("[DEPLOY] The following dependency files were found: "
+ f"{*self._dependency_files,}")
+
+ def _find_local_libs(self):
+ local_libs = set()
+ plugins = set()
+ lib_pattern = re.compile(f"lib(?P<lib_name>.*)_{self.arch}")
+ for dependency_file in self._dependency_files:
+ xml_content = dependency_file.read_text()
+ root = ET.fromstring(xml_content)
+ for local_lib in root.iter("lib"):
+
+ if 'file' not in local_lib.attrib:
+ if 'name' not in local_lib.attrib:
+ logging.warning("[DEPLOY] Invalid android dependency file"
+ f" {str(dependency_file)}")
+ continue
+
+ file = local_lib.attrib['file']
+ if file.endswith(".so"):
+ # file_name starts with lib and ends with the platform name
+ # eg: lib<lib_name>_x86_64.so
+ file_name = Path(file).stem
+
+ # we only need lib_name, because lib and arch gets re-added by
+ # python-for-android
+ match = lib_pattern.search(file_name)
+ if match:
+ lib_name = match.group("lib_name")
+ local_libs.add(lib_name)
+ if lib_name.startswith("plugins"):
+ plugin_name = lib_name.split('plugins_', 1)[1]
+ plugins.add(plugin_name)
+
+ return list(local_libs), list(plugins)
+
+ def _find_plugin_dependencies(self, dependent_plugins: List[str]):
+ # The `bundled` element in the dependency xml files points to the folder where
+ # additional dependencies for the application exists. Inspecting the depenency files
+ # in android, this always points to the specific Qt plugin dependency folder.
+ # eg: for application using Qt Multimedia, this looks like:
+ # <bundled file="./plugins/multimedia" />
+ # The code recusively checks all these dependent folders and adds the necessary plugins
+ # as dependencies
+ lib_pattern = re.compile(f"libplugins_(?P<plugin_name>.*)_{self.arch}.so")
+ for dependency_file in self._dependency_files:
+ xml_content = dependency_file.read_text()
+ root = ET.fromstring(xml_content)
+ for bundled_element in root.iter("bundled"):
+ # the attribute 'file' can be misleading, but it always points to the plugin
+ # folder on inspecting the dependency files
+ if 'file' not in bundled_element.attrib:
+ logging.warning("[DEPLOY] Invalid Android dependency file"
+ f" {str(dependency_file)}")
+ continue
+
+ # from "./plugins/multimedia" to absolute path in wheel
+ plugin_module_folder = bundled_element.attrib['file']
+ # they all should start with `./plugins`
+ if plugin_module_folder.startswith("./plugins"):
+ plugin_module_folder = plugin_module_folder.partition("./plugins/")[2]
+ else:
+ continue
+
+ absolute_plugin_module_folder = (self.qt_libs_path.parent / "plugins"
+ / plugin_module_folder)
+
+ if not absolute_plugin_module_folder.is_dir():
+ logging.warning(f"[DEPLOY] Qt plugin folder '{plugin_module_folder}' does not"
+ " exist or is not a directory for this Android platform")
+ continue
+
+ for plugin in absolute_plugin_module_folder.iterdir():
+ plugin_name = plugin.name
+ if plugin_name.endswith(".so") and plugin_name.startswith("libplugins"):
+ # we only need part of plugin_name, because `lib` prefix and `arch` suffix
+ # gets re-added by python-for-android
+ match = lib_pattern.search(plugin_name)
+ if match:
+ plugin_infix_name = match.group("plugin_name")
+ if plugin_infix_name not in dependent_plugins:
+ dependent_plugins.append(plugin_infix_name)
+
+ def verify_and_set_recipe_dir(self):
+ # create recipes
+ # https://python-for-android.readthedocs.io/en/latest/recipes/
+ # These recipes are manually added through buildozer.spec file to be used by
+ # python_for_android while building the distribution
+
+ if not self.recipes_exist() and not self.dry_run:
+ logging.info("[DEPLOY] Creating p4a recipes for PySide6 and shiboken6")
+ version = Wheel(self.wheel_pyside).version
+ create_recipe(version=version, component=f"PySide{MAJOR_VERSION}",
+ wheel_path=self.wheel_pyside,
+ generated_files_path=self.generated_files_path,
+ qt_modules=self.modules,
+ local_libs=self.local_libs,
+ plugins=self.qt_plugins)
+ create_recipe(version=version, component=f"shiboken{MAJOR_VERSION}",
+ wheel_path=self.wheel_shiboken,
+ generated_files_path=self.generated_files_path)
+ self.recipe_dir = ((self.generated_files_path
+ / "recipes").resolve())
if lib_path.name in used_dependencies:
return
+ used_dependencies.add(lib_path.name)
+
command = [str(llvm_readobj), "--needed-libs", str(lib_path)]
- _, output = run_command(command=command, dry_run=dry_run, fetch_output=True)
+
+ # even if dry_run is given, we need to run the actual command to see all the dependencies
+ # for which llvm-readelf is run.
+ if dry_run:
+ _, output = run_command(command=command, dry_run=dry_run, fetch_output=True)
+ _, output = run_command(command=command, dry_run=False, fetch_output=True)
dependencies = set()
neededlibraries_found = False
import sys
import logging
-import re
-import tempfile
import xml.etree.ElementTree as ET
import zipfile
-import PySide6
from pathlib import Path
from typing import List
-from pkginfo import Wheel
-
-from .. import MAJOR_VERSION, BaseConfig, Config, run_command
-from . import (create_recipe, find_lib_dependencies, find_qtlibs_in_wheel,
- get_llvm_readobj)
-
-# They all start with `Qt` as the prefix. Removing this prefix and getting the actual
-# module name
-ALL_PYSIDE_MODULES = [module[2:] for module in PySide6.__all__]
+from . import AndroidConfig
+from .. import BaseConfig, run_command
class BuildozerConfig(BaseConfig):
- def __init__(self, buildozer_spec_file: Path, pysidedeploy_config: Config):
+ def __init__(self, buildozer_spec_file: Path, pysidedeploy_config: AndroidConfig):
super().__init__(buildozer_spec_file, comment_prefixes="#")
self.set_value("app", "title", pysidedeploy_config.title)
self.set_value("app", "package.name", pysidedeploy_config.title)
if pysidedeploy_config.sdk_path:
self.set_value("app", "android.sdk_path", str(pysidedeploy_config.sdk_path))
- platform_map = {"aarch64": "arm64-v8a",
- "armv7a": "armeabi-v7a",
- "i686": "x86",
- "x86_64": "x86_64"}
- self.arch = platform_map[pysidedeploy_config.arch]
- self.set_value("app", "android.archs", self.arch)
+ self.set_value("app", "android.archs", pysidedeploy_config.arch)
# p4a changes
self.set_value("app", "p4a.bootstrap", "qt")
-
- self.qt_libs_path: zipfile.Path = (
- find_qtlibs_in_wheel(wheel_pyside=pysidedeploy_config.wheel_pyside))
- logging.info(f"[DEPLOY] Found Qt libs path inside wheel: {str(self.qt_libs_path)}")
-
- extra_modules = self.__find_dependent_qt_modules(pysidedeploy_config)
- logging.info(f"[DEPLOY] Other dependent modules to be added: {extra_modules}")
- pysidedeploy_config.modules = pysidedeploy_config.modules + extra_modules
- modules = ",".join(pysidedeploy_config.modules)
-
- # gets the xml dependency files from Qt installation path
- dependency_files = self.__get_dependency_files(modules=pysidedeploy_config.modules,
- arch=self.arch)
-
- dependent_plugins = []
- # the local_libs can also store dependent plugins
- local_libs, dependent_plugins = self.__find_local_libs(dependency_files)
- pysidedeploy_config.local_libs += local_libs
-
- self.__find_plugin_dependencies(dependency_files, dependent_plugins)
- pysidedeploy_config.qt_plugins += dependent_plugins
-
- local_libs = ",".join(pysidedeploy_config.local_libs)
-
- # create recipes
- # https://python-for-android.readthedocs.io/en/latest/recipes/
- # These recipes are manually added through buildozer.spec file to be used by
- # python_for_android while building the distribution
- if not pysidedeploy_config.recipes_exist() and not pysidedeploy_config.dry_run:
- logging.info("[DEPLOY] Creating p4a recipes for PySide6 and shiboken6")
- version = Wheel(pysidedeploy_config.wheel_pyside).version
- create_recipe(version=version, component=f"PySide{MAJOR_VERSION}",
- wheel_path=pysidedeploy_config.wheel_pyside,
- generated_files_path=pysidedeploy_config.generated_files_path,
- qt_modules=pysidedeploy_config.modules,
- local_libs=pysidedeploy_config.local_libs,
- plugins=pysidedeploy_config.qt_plugins)
- create_recipe(version=version, component=f"shiboken{MAJOR_VERSION}",
- wheel_path=pysidedeploy_config.wheel_shiboken,
- generated_files_path=pysidedeploy_config.generated_files_path)
- pysidedeploy_config.recipe_dir = ((pysidedeploy_config.generated_files_path
- / "recipes").resolve())
self.set_value('app', "p4a.local_recipes", str(pysidedeploy_config.recipe_dir))
+ # add p4a branch
+ # by default the master branch is used
+ # https://github.com/kivy/python-for-android/commit/b92522fab879dbfc0028966ca3c59ef46ab7767d
+ # has not been merged to master yet. So, we use the develop branch for now
+ # TODO: remove this once the above commit is merged to master
+ self.set_value("app", "p4a.branch", "develop")
+
# add permissions
- permissions = self.__find_permissions(dependency_files)
+ permissions = self.__find_permissions(pysidedeploy_config.dependency_files)
permissions = ", ".join(permissions)
self.set_value("app", "android.permissions", permissions)
# add jars and initClasses for the jars
- jars, init_classes = self.__find_jars(dependency_files, pysidedeploy_config.jars_dir)
+ jars, init_classes = self.__find_jars(pysidedeploy_config.dependency_files,
+ pysidedeploy_config.jars_dir)
self.set_value("app", "android.add_jars", ",".join(jars))
- init_classes = ",".join(init_classes)
# extra arguments specific to Qt
+ modules = ",".join(pysidedeploy_config.modules)
+ local_libs = ",".join(pysidedeploy_config.local_libs)
+ init_classes = ",".join(init_classes)
extra_args = (f"--qt-libs={modules} --load-local-libs={local_libs}"
f" --init-classes={init_classes}")
self.set_value("app", "p4a.extra_args", extra_args)
self.update_config()
- def __get_dependency_files(self, modules: List[str], arch: str) -> List[zipfile.Path]:
- """
- Based on pysidedeploy_config.modules, returns the
- Qt6{module}_{arch}-android-dependencies.xml file, which contains the various
- dependencies of the module, like permissions, plugins etc
- """
- dependency_files = []
- needed_dependency_files = [(f"Qt{MAJOR_VERSION}{module}_{arch}"
- "-android-dependencies.xml") for module in modules]
-
- for dependency_file_name in needed_dependency_files:
- dependency_file = self.qt_libs_path / dependency_file_name
- if dependency_file.exists():
- dependency_files.append(dependency_file)
-
- logging.info(f"[DEPLOY] The following dependency files were found: {*dependency_files,}")
-
- return dependency_files
-
def __find_permissions(self, dependency_files: List[zipfile.Path]):
permissions = set()
for dependency_file in dependency_files:
return jars, init_classes
- def __find_local_libs(self, dependency_files: List[zipfile.Path]):
- local_libs = set()
- plugins = set()
- lib_pattern = re.compile(f"lib(?P<lib_name>.*)_{self.arch}")
- for dependency_file in dependency_files:
- xml_content = dependency_file.read_text()
- root = ET.fromstring(xml_content)
- for local_lib in root.iter("lib"):
-
- if 'file' not in local_lib.attrib:
- if 'name' not in local_lib.attrib:
- logging.warning("[DEPLOY] Invalid android dependency file"
- f" {str(dependency_file)}")
- continue
-
- file = local_lib.attrib['file']
- if file.endswith(".so"):
- # file_name starts with lib and ends with the platform name
- # eg: lib<lib_name>_x86_64.so
- file_name = Path(file).stem
-
- if file_name.startswith("libplugins_platforms_qtforandroid"):
- # the platform library is a requisite and is already added from the
- # configuration file
- continue
-
- # we only need lib_name, because lib and arch gets re-added by
- # python-for-android
- match = lib_pattern.search(file_name)
- if match:
- lib_name = match.group("lib_name")
- local_libs.add(lib_name)
- if lib_name.startswith("plugins"):
- plugin_name = lib_name.split('plugins_', 1)[1]
- plugins.add(plugin_name)
-
- return list(local_libs), list(plugins)
-
- def __find_plugin_dependencies(self, dependency_files: List[zipfile.Path],
- dependent_plugins: List[str]):
- # The `bundled` element in the dependency xml files points to the folder where
- # additional dependencies for the application exists. Inspecting the depenency files
- # in android, this always points to the specific Qt plugin dependency folder.
- # eg: for application using Qt Multimedia, this looks like:
- # <bundled file="./plugins/multimedia" />
- # The code recusively checks all these dependent folders and adds the necessary plugins
- # as dependencies
- lib_pattern = re.compile(f"libplugins_(?P<plugin_name>.*)_{self.arch}.so")
- for dependency_file in dependency_files:
- xml_content = dependency_file.read_text()
- root = ET.fromstring(xml_content)
- for bundled_element in root.iter("bundled"):
- # the attribute 'file' can be misleading, but it always points to the plugin
- # folder on inspecting the dependency files
- if 'file' not in bundled_element.attrib:
- logging.warning("[DEPLOY] Invalid Android dependency file"
- f" {str(dependency_file)}")
- continue
-
- # from "./plugins/multimedia" to absolute path in wheel
- plugin_module_folder = bundled_element.attrib['file']
- # they all should start with `./plugins`
- if plugin_module_folder.startswith("./plugins"):
- plugin_module_folder = plugin_module_folder.partition("./plugins/")[2]
- else:
- continue
-
- absolute_plugin_module_folder = (self.qt_libs_path.parent / "plugins"
- / plugin_module_folder)
-
- if not absolute_plugin_module_folder.is_dir():
- logging.warning(f"[DEPLOY] Qt plugin folder '{plugin_module_folder}' does not"
- " exist or is not a directory for this Android platform")
- continue
-
- for plugin in absolute_plugin_module_folder.iterdir():
- plugin_name = plugin.name
- if plugin_name.endswith(".so") and plugin_name.startswith("libplugins"):
- # we only need part of plugin_name, because `lib` prefix and `arch` suffix
- # gets re-added by python-for-android
- match = lib_pattern.search(plugin_name)
- if match:
- plugin_infix_name = match.group("plugin_name")
- if plugin_infix_name not in dependent_plugins:
- dependent_plugins.append(plugin_infix_name)
-
- def __find_dependent_qt_modules(self, pysidedeploy_config: Config):
- """
- Given pysidedeploy_config.modules, find all the other dependent Qt modules. This is
- done by using llvm-readobj (readelf) to find the dependent libraries from the module
- library.
- """
- dependent_modules = set()
- all_dependencies = set()
- lib_pattern = re.compile(f"libQt6(?P<mod_name>.*)_{self.arch}")
-
- llvm_readobj = get_llvm_readobj(pysidedeploy_config.ndk_path)
- if not llvm_readobj.exists():
- raise FileNotFoundError(f"[DEPLOY] {llvm_readobj} does not exist."
- "Finding Qt dependencies failed")
-
- archive = zipfile.ZipFile(pysidedeploy_config.wheel_pyside)
- lib_path_suffix = Path(str(self.qt_libs_path)).relative_to(pysidedeploy_config.wheel_pyside)
-
- with tempfile.TemporaryDirectory() as tmpdir:
- archive.extractall(tmpdir)
- qt_libs_tmpdir = Path(tmpdir) / lib_path_suffix
- # find the lib folder where Qt libraries are stored
- for module_name in pysidedeploy_config.modules:
- qt_module_path = qt_libs_tmpdir / f"libQt6{module_name}_{self.arch}.so"
- if not qt_module_path.exists():
- raise FileNotFoundError(f"[DEPLOY] libQt6{module_name}_{self.arch}.so not found"
- " inside the wheel")
- find_lib_dependencies(llvm_readobj=llvm_readobj, lib_path=qt_module_path,
- dry_run=pysidedeploy_config.dry_run,
- used_dependencies=all_dependencies)
-
- for dependency in all_dependencies:
- match = lib_pattern.search(dependency)
- if match:
- module = match.group("mod_name")
- if module not in pysidedeploy_config.modules:
- dependent_modules.add(module)
-
- # check if the PySide6 binary for the Qt module actually exists
- # eg: libQt6QmlModels.so exists and it includes QML types. Hence, it makes no
- dependent_modules = [module for module in dependent_modules if module in ALL_PYSIDE_MODULES]
- dependent_modules_str = ",".join(dependent_modules)
- logging.info("[DEPLOY] The following extra dependencies were found:"
- f" {dependent_modules_str}")
-
- return list(dependent_modules)
-
class Buildozer:
dry_run = False
@staticmethod
- def initialize(pysidedeploy_config: Config):
+ def initialize(pysidedeploy_config: AndroidConfig):
project_dir = Path(pysidedeploy_config.project_dir)
buildozer_spec = project_dir / "buildozer.spec"
if buildozer_spec.exists():
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+import sys
import configparser
import logging
import warnings
from configparser import ConfigParser
+from typing import List
from pathlib import Path
from project import ProjectData
-
-from .commands import run_qmlimportscanner
-from . import DEFAULT_APP_ICON
+from . import (DEFAULT_APP_ICON, find_pyside_modules, find_permission_categories,
+ QtDependencyReader, run_qmlimportscanner)
# Some QML plugins like QtCore are excluded from this list as they don't contribute much to
# executable size. Excluding them saves the extra processing of checking for them in files
EXCLUDED_QML_PLUGINS = {"QtQuick", "QtQuick3D", "QtCharts", "QtWebEngine", "QtTest", "QtSensors"}
+PERMISSION_MAP = {"Bluetooth": "NSBluetoothAlwaysUsageDescription:BluetoothAccess",
+ "Camera": "NSCameraUsageDescription:CameraAccess",
+ "Microphone": "NSMicrophoneUsageDescription:MicrophoneAccess",
+ "Contacts": "NSContactsUsageDescription:ContactsAccess",
+ "Calendar": "NSCalendarsUsageDescription:CalendarAccess",
+ # for iOS NSLocationWhenInUseUsageDescription and
+ # NSLocationAlwaysAndWhenInUseUsageDescription are also required.
+ "Location": "NSLocationUsageDescription:LocationAccess",
+ }
-class BaseConfig:
+class BaseConfig:
+ """Wrapper class around any .spec file with function to read and set values for the .spec file
+ """
def __init__(self, config_file: Path, comment_prefixes: str = "/",
existing_config_file: bool = False) -> None:
self.config_file = config_file
self.existing_config_file = existing_config_file
- self.parser = ConfigParser(comment_prefixes=comment_prefixes, allow_no_value=True)
+ self.parser = ConfigParser(comment_prefixes=comment_prefixes, strict=False,
+ allow_no_value=True)
self.parser.read(self.config_file)
def update_config(self):
"""
def __init__(self, config_file: Path, source_file: Path, python_exe: Path, dry_run: bool,
- existing_config_file: bool = False):
+ existing_config_file: bool = False, extra_ignore_dirs: List[str] = None):
super().__init__(config_file=config_file, existing_config_file=existing_config_file)
+ self.extra_ignore_dirs = extra_ignore_dirs
self._dry_run = dry_run
self.qml_modules = set()
# set source_file
self._generated_files_path = self.project_dir / "deployment"
+ self.modules = []
+
def set_or_fetch(self, config_property_val, config_property_key, config_property_group="app"):
"""
Write to config_file if 'config_property_key' is known without config_file
def exe_dir(self, exe_dir: Path):
self._exe_dir = exe_dir
+ @property
+ def modules(self):
+ return self._modules
+
+ @modules.setter
+ def modules(self, modules):
+ self._modules = modules
+ self.set_value("qt", "modules", ",".join(modules))
+
def _find_and_set_qml_files(self):
"""Fetches all the qml_files in the folder and sets them if the
field qml_files is empty in the config_dir"""
self.qml_files = qml_files
else:
qml_files_temp = None
- source_file = (
- Path(self.get_value("app", "input_file"))
- if self.get_value("app", "input_file")
- else None
- )
- python_exe = (
- Path(self.get_value("python", "python_path"))
- if self.get_value("python", "python_path")
- else None
- )
- if source_file and python_exe:
+ if self.source_file and self.python_path:
if not self.qml_files:
- qml_files_temp = list(source_file.parent.glob("**/*.qml"))
+ qml_files_temp = list(self.source_file.parent.glob("**/*.qml"))
# add all QML files, excluding the ones shipped with installed PySide6
# The QML files shipped with PySide6 gets added if venv is used,
# because of recursive glob
- if python_exe.parent.parent == source_file.parent:
+ if self.python_path.parent.parent == self.source_file.parent:
# python venv path is inside the main source dir
qml_files_temp = list(
- set(qml_files_temp) - set(python_exe.parent.parent.rglob("*.qml"))
+ set(qml_files_temp) - set(self.python_path.parent.parent.rglob("*.qml"))
)
if len(qml_files_temp) > 500:
config_property_val=self.exe_dir, config_property_key="exec_directory"
)
).absolute()
+
+ def _find_and_set_pysidemodules(self):
+ self.modules = find_pyside_modules(project_dir=self.project_dir,
+ extra_ignore_dirs=self.extra_ignore_dirs,
+ project_data=self.project_data)
+ logging.info("The following PySide modules were found from the Python files of "
+ f"the project {self.modules}")
+
+ def _find_and_set_qtquick_modules(self):
+ """Identify if QtQuick is used in QML files and add them as dependency
+ """
+ extra_modules = []
+ if not self.qml_modules:
+ self.qml_modules = set(run_qmlimportscanner(qml_files=self.qml_files,
+ dry_run=self.dry_run))
+
+ if "QtQuick" in self.qml_modules:
+ extra_modules.append("Quick")
+
+ if "QtQuick.Controls" in self.qml_modules:
+ extra_modules.append("QuickControls2")
+
+ self.modules += extra_modules
+
+
+class DesktopConfig(Config):
+ """Wrapper class around pysidedeploy.spec, but specific to Desktop deployment
+ """
+ def __init__(self, config_file: Path, source_file: Path, python_exe: Path, dry_run: bool,
+ existing_config_file: bool = False, extra_ignore_dirs: List[str] = None):
+ super().__init__(config_file, source_file, python_exe, dry_run, existing_config_file,
+ extra_ignore_dirs)
+ self.dependency_reader = QtDependencyReader(dry_run=self.dry_run)
+ if self.get_value("qt", "modules"):
+ self.modules = self.get_value("qt", "modules").split(",")
+ else:
+ self._find_and_set_pysidemodules()
+ self._find_and_set_qtquick_modules()
+ self._find_dependent_qt_modules()
+
+ self._qt_plugins = []
+ if self.get_value("qt", "plugins"):
+ self._qt_plugins = self.get_value("qt", "plugins").split(",")
+ else:
+ self.qt_plugins = self.dependency_reader.find_plugin_dependencies(self.modules,
+ python_exe)
+
+ self._permissions = []
+ if sys.platform == "darwin":
+ nuitka_macos_permissions = self.get_value("nuitka", "macos.permissions")
+ if nuitka_macos_permissions:
+ self._permissions = nuitka_macos_permissions.split(",")
+ else:
+ self._find_and_set_permissions()
+
+ @property
+ def qt_plugins(self):
+ return self._qt_plugins
+
+ @qt_plugins.setter
+ def qt_plugins(self, qt_plugins):
+ self._qt_plugins = qt_plugins
+ self.set_value("qt", "plugins", ",".join(qt_plugins))
+
+ @property
+ def permissions(self):
+ return self._permissions
+
+ @permissions.setter
+ def permissions(self, permissions):
+ self._permissions = permissions
+ self.set_value("nuitka", "macos.permissions", ",".join(permissions))
+
+ def _find_dependent_qt_modules(self):
+ """
+ Given pysidedeploy_config.modules, find all the other dependent Qt modules.
+ """
+ all_modules = set(self.modules)
+
+ if not self.dependency_reader.lib_reader:
+ warnings.warn(f"[DEPLOY] Unable to find {self.dependency_reader.lib_reader_name}. This "
+ "tool helps to find the Qt module dependencies of the application. "
+ "Skipping checking for dependencies.", category=RuntimeWarning)
+ return
+
+ for module_name in self.modules:
+ self.dependency_reader.find_dependencies(module=module_name, used_modules=all_modules)
+
+ self.modules = list(all_modules)
+
+ def _find_and_set_permissions(self):
+ """
+ Finds and sets the usage description string required for each permission requested by the
+ macOS application.
+ """
+ permissions = []
+ perm_categories = find_permission_categories(project_dir=self.project_dir,
+ extra_ignore_dirs=self.extra_ignore_dirs,
+ project_data=self.project_data)
+
+ perm_categories_str = ",".join(perm_categories)
+ logging.info(f"[DEPLOY] Usage descriptions for the {perm_categories_str} will be added to "
+ "the Info.plist file of the macOS application bundle")
+
+ # handling permissions
+ for perm_category in perm_categories:
+ if perm_category in PERMISSION_MAP:
+ permissions.append(PERMISSION_MAP[perm_category])
+
+ self.permissions = permissions
# python packages to install
# ordered-set: increase compile time performance of nuitka packaging
# zstandard: provides final executable size optimization
-packages = nuitka==1.8.0,ordered_set,zstandard
+packages = Nuitka==2.3.2
# buildozer: for deploying Android application
android_packages = buildozer==1.5.0,cython==0.29.33
# excluded qml plugin binaries
excluded_qml_plugins =
+# Qt modules used. Comma separated
+modules =
+
+# Qt plugins used by the application
+plugins =
+
[android]
# path to PySide wheel
wheel_shiboken =
# plugins to be copied to libs folder of the packaged application. Comma separated
-plugins = platforms_qtforandroid
+plugins =
[nuitka]
+# usage description for permissions requested by the app as found in the Info.plist file
+# of the app bundle
+# eg: NSCameraUsageDescription:CameraAccess
+macos.permissions =
+
# (str) specify any extra nuitka arguments
# eg: extra_args = --show-modules --follow-stdlib
extra_args = --quiet --noinclude-qt-translations
# if empty uses default sdk path downloaded by buildozer
sdk_path =
-# modules used. Comma separated
-modules =
-
# other libraries to be loaded. Comma separated.
# loaded at app startup
-local_libs = plugins_platforms_qtforandroid
+local_libs =
# architecture of deployed platform
# possible values: ["aarch64", "armv7a", "i686", "x86_64"]
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import ast
+import re
+import os
+import site
+import json
+import warnings
+import logging
+import shutil
+import sys
+from pathlib import Path
+from typing import List, Set
+from functools import lru_cache
+
+from . import IMPORT_WARNING_PYSIDE, run_command
+
+
+@lru_cache(maxsize=None)
+def get_py_files(project_dir: Path, extra_ignore_dirs: List[Path] = None, project_data=None):
+ """Finds and returns all the Python files in the project
+ """
+ py_candidates = []
+ ignore_dirs = ["__pycache__", "env", "venv", "deployment"]
+
+ if project_data:
+ py_candidates = project_data.python_files
+ ui_candidates = project_data.ui_files
+ qrc_candidates = project_data.qrc_files
+
+ def add_uic_qrc_candidates(candidates, candidate_type):
+ possible_py_candidates = [(file.parent / f"{candidate_type}_{file.stem}.py")
+ for file in candidates
+ if (file.parent / f"{candidate_type}_{file.stem}.py").exists()
+ ]
+
+ if len(possible_py_candidates) != len(candidates):
+ warnings.warn(f"[DEPLOY] The number of {candidate_type} files and their "
+ "corresponding Python files don't match.",
+ category=RuntimeWarning)
+
+ py_candidates.extend(possible_py_candidates)
+
+ if ui_candidates:
+ add_uic_qrc_candidates(ui_candidates, "ui")
+
+ if qrc_candidates:
+ add_uic_qrc_candidates(qrc_candidates, "qrc")
+
+ return py_candidates
+
+ # incase there is not .pyproject file, search all python files in project_dir, except
+ # ignore_dirs
+ if extra_ignore_dirs:
+ ignore_dirs.extend(extra_ignore_dirs)
+
+ # find relevant .py files
+ _walk = os.walk(project_dir)
+ for root, dirs, files in _walk:
+ dirs[:] = [d for d in dirs if d not in ignore_dirs and not d.startswith(".")]
+ for py_file in files:
+ if py_file.endswith(".py"):
+ py_candidates.append(Path(root) / py_file)
+
+ return py_candidates
+
+
+@lru_cache(maxsize=None)
+def get_ast(py_file: Path):
+ """Given a Python file returns the abstract syntax tree
+ """
+ contents = py_file.read_text(encoding="utf-8")
+ try:
+ tree = ast.parse(contents)
+ except SyntaxError:
+ print(f"[DEPLOY] Unable to parse {py_file}")
+ return tree
+
+
+def find_permission_categories(project_dir: Path, extra_ignore_dirs: List[Path] = None,
+ project_data=None):
+ """Given the project directory, finds all the permission categories required by the
+ project. eg: Camera, Bluetooth, Contacts etc.
+
+ Note: This function is only relevant for mac0S deployment.
+ """
+ all_perm_categories = set()
+ mod_pattern = re.compile("Q(?P<mod_name>.*)Permission")
+
+ def pyside_permission_imports(py_file: Path):
+ perm_categories = []
+ try:
+ tree = get_ast(py_file)
+ for node in ast.walk(tree):
+ if isinstance(node, ast.ImportFrom):
+ main_mod_name = node.module
+ if main_mod_name == "PySide6.QtCore":
+ # considers 'from PySide6.QtCore import QtMicrophonePermission'
+ for imported_module in node.names:
+ full_mod_name = imported_module.name
+ match = mod_pattern.search(full_mod_name)
+ if match:
+ mod_name = match.group("mod_name")
+ perm_categories.append(mod_name)
+ continue
+
+ if isinstance(node, ast.Import):
+ for imported_module in node.names:
+ full_mod_name = imported_module.name
+ if full_mod_name == "PySide6":
+ logging.warning(IMPORT_WARNING_PYSIDE.format(str(py_file)))
+ except Exception as e:
+ raise RuntimeError(f"[DEPLOY] Finding permission categories failed on file "
+ f"{str(py_file)} with error {e}")
+
+ return set(perm_categories)
+
+ py_candidates = get_py_files(project_dir, extra_ignore_dirs, project_data)
+ for py_candidate in py_candidates:
+ all_perm_categories = all_perm_categories.union(pyside_permission_imports(py_candidate))
+
+ if not all_perm_categories:
+ ValueError("[DEPLOY] No permission categories were found for macOS app bundle creation.")
+
+ return all_perm_categories
+
+
+def find_pyside_modules(project_dir: Path, extra_ignore_dirs: List[Path] = None,
+ project_data=None):
+ """
+ Searches all the python files in the project to find all the PySide modules used by
+ the application.
+ """
+ all_modules = set()
+ mod_pattern = re.compile("PySide6.Qt(?P<mod_name>.*)")
+
+ def pyside_module_imports(py_file: Path):
+ modules = []
+ try:
+ tree = get_ast(py_file)
+ for node in ast.walk(tree):
+ if isinstance(node, ast.ImportFrom):
+ main_mod_name = node.module
+ if main_mod_name.startswith("PySide6"):
+ if main_mod_name == "PySide6":
+ # considers 'from PySide6 import QtCore'
+ for imported_module in node.names:
+ full_mod_name = imported_module.name
+ if full_mod_name.startswith("Qt"):
+ modules.append(full_mod_name[2:])
+ continue
+
+ # considers 'from PySide6.QtCore import Qt'
+ match = mod_pattern.search(main_mod_name)
+ if match:
+ mod_name = match.group("mod_name")
+ modules.append(mod_name)
+ else:
+ logging.warning((
+ f"[DEPLOY] Unable to find module name from {ast.dump(node)}"))
+
+ if isinstance(node, ast.Import):
+ for imported_module in node.names:
+ full_mod_name = imported_module.name
+ if full_mod_name == "PySide6":
+ logging.warning(IMPORT_WARNING_PYSIDE.format(str(py_file)))
+ except Exception as e:
+ raise RuntimeError(f"[DEPLOY] Finding module import failed on file {str(py_file)} with "
+ f"error {e}")
+
+ return set(modules)
+
+ py_candidates = get_py_files(project_dir, extra_ignore_dirs, project_data)
+ for py_candidate in py_candidates:
+ all_modules = all_modules.union(pyside_module_imports(py_candidate))
+
+ if not all_modules:
+ ValueError("[DEPLOY] No PySide6 modules were found")
+
+ return list(all_modules)
+
+
+class QtDependencyReader:
+ def __init__(self, dry_run: bool = False) -> None:
+ self.dry_run = dry_run
+ self.lib_reader_name = None
+ self.qt_module_path_pattern = None
+ self.lib_pattern = None
+ self.command = None
+ self.qt_libs_dir = None
+
+ if sys.platform == "linux":
+ self.lib_reader_name = "readelf"
+ self.qt_module_path_pattern = "libQt6{module}.so.6"
+ self.lib_pattern = re.compile("libQt6(?P<mod_name>.*).so.6")
+ self.command_args = "-d"
+ elif sys.platform == "darwin":
+ self.lib_reader_name = "dyld_info"
+ self.qt_module_path_pattern = "Qt{module}.framework/Versions/A/Qt{module}"
+ self.lib_pattern = re.compile("@rpath/Qt(?P<mod_name>.*).framework/Versions/A/")
+ self.command_args = "-dependents"
+ elif sys.platform == "win32":
+ self.lib_reader_name = "dumpbin"
+ self.qt_module_path_pattern = "Qt6{module}.dll"
+ self.lib_pattern = re.compile("Qt6(?P<mod_name>.*).dll")
+ self.command_args = "/dependents"
+ else:
+ print(f"[DEPLOY] Deployment on unsupported platfrom {sys.platform}")
+ sys.exit(1)
+
+ self.pyside_install_dir = None
+ self.qt_libs_dir = self.get_qt_libs_dir()
+ self._lib_reader = shutil.which(self.lib_reader_name)
+
+ def get_qt_libs_dir(self):
+ """
+ Finds the path to the Qt libs directory inside PySide6 package installation
+ """
+ for possible_site_package in site.getsitepackages():
+ if possible_site_package.endswith("site-packages"):
+ self.pyside_install_dir = Path(possible_site_package) / "PySide6"
+
+ if not self.pyside_install_dir:
+ print("Unable to find site-packages. Exiting ...")
+ sys.exit(-1)
+
+ if sys.platform == "win32":
+ return self.pyside_install_dir
+
+ return self.pyside_install_dir / "Qt" / "lib" # for linux and macOS
+
+ @property
+ def lib_reader(self):
+ return self._lib_reader
+
+ def find_dependencies(self, module: str, used_modules: Set[str] = None):
+ """
+ Given a Qt module, find all the other Qt modules it is dependent on and add it to the
+ 'used_modules' set
+ """
+ qt_module_path = self.qt_libs_dir / self.qt_module_path_pattern.format(module=module)
+ if not qt_module_path.exists():
+ warnings.warn(f"[DEPLOY] {qt_module_path.name} not found in {str(qt_module_path)}."
+ "Skipping finding its dependencies.", category=RuntimeWarning)
+ return
+
+ lib_pattern = re.compile(self.lib_pattern)
+ command = [self.lib_reader, self.command_args, str(qt_module_path)]
+ # print the command if dry_run is True.
+ # Normally run_command is going to print the command in dry_run mode. But, this is a
+ # special case where we need to print the command as well as to run it.
+ if self.dry_run:
+ command_str = " ".join(command)
+ print(command_str + "\n")
+
+ # We need to run this even for dry run, to see the full Nuitka command being executed
+ _, output = run_command(command=command, dry_run=False, fetch_output=True)
+
+ dependent_modules = set()
+ for line in output.splitlines():
+ line = line.decode("utf-8").lstrip()
+ if sys.platform == "darwin":
+ if line.endswith(f"Qt{module} [arm64]:"):
+ # macOS Qt frameworks bundles have both x86_64 and arm64 architectures
+ # We only need to consider one as the dependencies are redundant
+ break
+ elif line.endswith(f"Qt{module} [X86_64]:"):
+ # this line needs to be skipped because it matches with the pattern
+ # and is related to the module itself, not the dependencies of the module
+ continue
+ elif sys.platform == "win32" and line.startswith("Summary"):
+ # the dependencies would be found before the `Summary` line
+ break
+ match = lib_pattern.search(line)
+ if match:
+ dep_module = match.group("mod_name")
+ dependent_modules.add(dep_module)
+ if dep_module not in used_modules:
+ used_modules.add(dep_module)
+ self.find_dependencies(module=dep_module, used_modules=used_modules)
+
+ if dependent_modules:
+ logging.info(f"[DEPLOY] Following dependencies found for {module}: {dependent_modules}")
+ else:
+ logging.info(f"[DEPLOY] No Qt dependencies found for {module}")
+
+ def find_plugin_dependencies(self, used_modules: List[str], python_exe: Path) -> List[str]:
+ """
+ Given the modules used by the application, returns all the required plugins
+ """
+ plugins = set()
+ pyside_wheels = ["PySide6_Essentials", "PySide6_Addons"]
+ # TODO from 3.12 use list(dist.name for dist in importlib.metadata.distributions())
+ _, installed_packages = run_command(command=[str(python_exe), "-m", "pip", "freeze"],
+ dry_run=False, fetch_output=True)
+ installed_packages = [p.decode().split('==')[0] for p in installed_packages.split()]
+ for pyside_wheel in pyside_wheels:
+ if pyside_wheel not in installed_packages:
+ # the wheel is not installed and hence no plugins are checked for its modules
+ logging.warning((f"[DEPLOY] The package {pyside_wheel} is not installed. "))
+ continue
+ pyside_mod_plugin_json_name = f"{pyside_wheel}.json"
+ pyside_mod_plugin_json_file = self.pyside_install_dir / pyside_mod_plugin_json_name
+ if not pyside_mod_plugin_json_file.exists():
+ warnings.warn(f"[DEPLOY] Unable to find {pyside_mod_plugin_json_file}.",
+ category=RuntimeWarning)
+ continue
+
+ # convert the json to dict
+ pyside_mod_dict = {}
+ with open(pyside_mod_plugin_json_file) as pyside_json:
+ pyside_mod_dict = json.load(pyside_json)
+
+ # find all the plugins in the modules
+ for module in used_modules:
+ plugins.update(pyside_mod_dict.get(module, []))
+
+ return list(plugins)
from . import EXE_FORMAT
from .config import Config
-from .python_helper import PythonExecutable
def config_option_exists():
return config_file
-def setup_python(dry_run: bool, force: bool, init: bool):
- """
- Sets up Python venv for deployment, and return a wrapper around the venv environment
- """
- python = None
- response = "yes"
- # checking if inside virtual environment
- if not PythonExecutable.is_venv() and not force and not dry_run and not init:
- response = input(("You are not using a virtual environment. pyside6-deploy needs to install"
- " a few Python packages for deployment to work seamlessly. \n"
- "Proceed? [Y/n]"))
-
- if response.lower() in ["no", "n"]:
- print("[DEPLOY] Exiting ...")
- sys.exit(0)
-
- python = PythonExecutable(dry_run=dry_run)
- logging.info(f"[DEPLOY] Using python at {sys.executable}")
-
- return python
-
-
-def install_python_dependencies(config: Config, python: PythonExecutable, init: bool,
- packages: str, is_android: bool = False):
- """
- Installs the python package dependencies for the target deployment platform
- """
- packages = config.get_value("python", packages).split(",")
- if not init:
- # install packages needed for deployment
- logging.info("[DEPLOY] Installing dependencies")
- python.install(packages=packages)
- # nuitka requires patchelf to make patchelf rpath changes for some Qt files
- if sys.platform.startswith("linux") and not is_android:
- python.install(packages=["patchelf"])
- elif is_android:
- # install only buildozer
- logging.info("[DEPLOY] Installing buildozer")
- buildozer_package_with_version = ([package for package in packages
- if package.startswith("buildozer")])
- python.install(packages=list(buildozer_package_with_version))
-
-
def finalize(config: Config):
"""
Copy the executable into the final location
"""
generated_exec_path = config.generated_files_path / (config.source_file.stem + EXE_FORMAT)
if generated_exec_path.exists() and config.exe_dir:
- shutil.copy(generated_exec_path, config.exe_dir)
+ if sys.platform == "darwin":
+ shutil.copytree(generated_exec_path, config.exe_dir / (config.title + EXE_FORMAT),
+ dirs_exist_ok=True)
+ else:
+ shutil.copy(generated_exec_path, config.exe_dir)
print("[DEPLOY] Executed file created in "
f"{str(config.exe_dir / (config.source_file.stem + EXE_FORMAT))}")
def __init__(self, nuitka):
self.nuitka = nuitka
+ # plugins to ignore. The sensible plugins are include by default by Nuitka for PySide6
+ # application deployment
+ self.qt_plugins_to_ignore = ["imageformats", # being Nuitka `sensible`` plugins
+ "iconengines",
+ "mediaservice",
+ "printsupport",
+ "platforms",
+ "platformthemes",
+ "styles",
+ "wayland-shell-integration",
+ "wayland-decoration-client",
+ "wayland-graphics-integration-client",
+ "egldeviceintegrations",
+ "xcbglintegrations",
+ "tls", # end Nuitka `sensible` plugins
+ "generic" # plugins that error with Nuitka
+ ]
+
+ # .webp are considered to be dlls by Nuitka instead of data files causing
+ # the packaging to fail
+ # https://github.com/Nuitka/Nuitka/issues/2854
+ # TODO: Remove .webp when the issue is fixed
+ self.files_to_ignore = [".cpp.o", ".qsb", ".webp"]
@staticmethod
def icon_option():
return "--macos-app-icon"
def create_executable(self, source_file: Path, extra_args: str, qml_files: List[Path],
- excluded_qml_plugins: List[str], icon: str, dry_run: bool):
+ qt_plugins: List[str], excluded_qml_plugins: List[str], icon: str,
+ dry_run: bool, permissions: List[str]):
+ qt_plugins = [plugin for plugin in qt_plugins if plugin not in self.qt_plugins_to_ignore]
extra_args = extra_args.split()
+
+ if sys.platform == "darwin":
+ # create an app bundle
+ extra_args.extend(["--standalone", "--macos-create-app-bundle"])
+ permission_pattern = "--macos-app-protected-resource={permission}"
+ for permission in permissions:
+ extra_args.append(permission_pattern.format(permission=permission))
+ else:
+ extra_args.append("--onefile")
+
qml_args = []
if qml_files:
- qml_args.append("--include-qt-plugins=all")
# This will generate options for each file using:
# --include-data-files=ABSOLUTE_PATH_TO_FILE=RELATIVE_PATH_TO ROOT
# for each file. This will preserve the directory structure of QML resources.
f"./{qml_file.resolve().relative_to(source_file.parent)}"
for qml_file in qml_files]
)
+ # add qml plugin. The `qml`` plugin name is not present in the module json files shipped
+ # with Qt and hence not in `qt_plugins``. However, Nuitka uses the 'qml' plugin name to
+ # include the necessary qml plugins. There we have to add it explicitly for a qml
+ # application
+ qt_plugins.append("qml")
if excluded_qml_plugins:
prefix = "lib" if sys.platform != "win32" else ""
dll_name = plugin.replace("Qt", f"Qt{MAJOR_VERSION}")
qml_args.append(f"--noinclude-dlls={prefix}{dll_name}*")
+ # Exclude .qen json files from QtQuickEffectMaker
+ # These files are not relevant for PySide6 applications
+ qml_args.append("--noinclude-dlls=*/qml/QtQuickEffectMaker/*")
+
+ # Exclude files that cannot be processed by Nuitka
+ for file in self.files_to_ignore:
+ extra_args.append(f"--noinclude-dlls=*{file}")
+
output_dir = source_file.parent / "deployment"
if not dry_run:
output_dir.mkdir(parents=True, exist_ok=True)
command = self.nuitka + [
os.fspath(source_file),
"--follow-imports",
- "--onefile",
"--enable-plugin=pyside6",
f"--output-dir={output_dir}",
]
+
command.extend(extra_args + qml_args)
command.append(f"{self.__class__.icon_option()}={icon}")
+ if qt_plugins:
+ # sort qt_plugins so that the result is definitive when testing
+ qt_plugins.sort()
+ qt_plugins_str = ",".join(qt_plugins)
+ command.append(f"--include-qt-plugins={qt_plugins_str}")
command_str, _ = run_command(command=command, dry_run=dry_run)
return command_str
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-import ast
import logging
import os
-import re
import sys
-import warnings
-from typing import List
+
from importlib import util
from importlib.metadata import version
from pathlib import Path
-from . import Nuitka, run_command
-
-IMPORT_WARNING_PYSIDE = (f"[DEPLOY] Found 'import PySide6' in file {0}"
- ". Use 'from PySide6 import <module>' or pass the module"
- " needed using --extra-modules command line argument")
-
-
-def find_pyside_modules(project_dir: Path, extra_ignore_dirs: List[Path] = None,
- project_data=None):
- """
- Searches all the python files in the project to find all the PySide modules used by
- the application.
- """
- all_modules = set()
- mod_pattern = re.compile("PySide6.Qt(?P<mod_name>.*)")
-
- def pyside_imports(py_file: Path):
- modules = []
- contents = py_file.read_text(encoding="utf-8")
- try:
- tree = ast.parse(contents)
- for node in ast.walk(tree):
- if isinstance(node, ast.ImportFrom):
- main_mod_name = node.module
- if main_mod_name.startswith("PySide6"):
- if main_mod_name == "PySide6":
- # considers 'from PySide6 import QtCore'
- for imported_module in node.names:
- full_mod_name = imported_module.name
- if full_mod_name.startswith("Qt"):
- modules.append(full_mod_name[2:])
- continue
-
- # considers 'from PySide6.QtCore import Qt'
- match = mod_pattern.search(main_mod_name)
- if match:
- mod_name = match.group("mod_name")
- modules.append(mod_name)
- else:
- logging.warning((
- f"[DEPLOY] Unable to find module name from{ast.dump(node)}"))
-
- if isinstance(node, ast.Import):
- for imported_module in node.names:
- full_mod_name = imported_module.name
- if full_mod_name == "PySide6":
- logging.warning(IMPORT_WARNING_PYSIDE.format(str(py_file)))
- except Exception as e:
- raise RuntimeError(f"[DEPLOY] Finding module import failed on file {str(py_file)} with "
- f"error {e}")
-
- return set(modules)
-
- py_candidates = []
- ignore_dirs = ["__pycache__", "env", "venv", "deployment"]
-
- if project_data:
- py_candidates = project_data.python_files
- ui_candidates = project_data.ui_files
- qrc_candidates = project_data.qrc_files
- ui_py_candidates = None
- qrc_ui_candidates = None
-
- if ui_candidates:
- ui_py_candidates = [(file.parent / f"ui_{file.stem}.py") for file in ui_candidates
- if (file.parent / f"ui_{file.stem}.py").exists()]
-
- if len(ui_py_candidates) != len(ui_candidates):
- warnings.warn("[DEPLOY] The number of uic files and their corresponding Python"
- " files don't match.", category=RuntimeWarning)
-
- py_candidates.extend(ui_py_candidates)
-
- if qrc_candidates:
- qrc_ui_candidates = [(file.parent / f"rc_{file.stem}.py") for file in qrc_candidates
- if (file.parent / f"rc_{file.stem}.py").exists()]
-
- if len(qrc_ui_candidates) != len(qrc_candidates):
- warnings.warn("[DEPLOY] The number of qrc files and their corresponding Python"
- " files don't match.", category=RuntimeWarning)
-
- py_candidates.extend(qrc_ui_candidates)
-
- for py_candidate in py_candidates:
- all_modules = all_modules.union(pyside_imports(py_candidate))
- return list(all_modules)
-
- # incase there is not .pyproject file, search all python files in project_dir, except
- # ignore_dirs
- if extra_ignore_dirs:
- ignore_dirs.extend(extra_ignore_dirs)
-
- # find relevant .py files
- _walk = os.walk(project_dir)
- for root, dirs, files in _walk:
- dirs[:] = [d for d in dirs if d not in ignore_dirs and not d.startswith(".")]
- for py_file in files:
- if py_file.endswith(".py"):
- py_candidates.append(Path(root) / py_file)
-
- for py_candidate in py_candidates:
- all_modules = all_modules.union(pyside_imports(py_candidate))
-
- if not all_modules:
- ValueError("[DEPLOY] No PySide6 modules were found")
-
- return list(all_modules)
+from . import Config, run_command
class PythonExecutable:
Wrapper class around Python executable
"""
- def __init__(self, python_path=None, dry_run=False):
- self.exe = python_path if python_path else Path(sys.executable)
+ def __init__(self, python_path: Path = None, dry_run: bool = False, init: bool = False,
+ force: bool = False):
+
self.dry_run = dry_run
- self.nuitka = Nuitka(nuitka=[os.fspath(self.exe), "-m", "nuitka"])
+ self.init = init
+ if not python_path:
+ response = "yes"
+ # checking if inside virtual environment
+ if not self.is_venv() and not force and not self.dry_run and not self.init:
+ response = input(("You are not using a virtual environment. pyside6-deploy needs "
+ "to install a few Python packages for deployment to work "
+ "seamlessly. \n Proceed? [Y/n]"))
+
+ if response.lower() in ["no", "n"]:
+ print("[DEPLOY] Exiting ...")
+ sys.exit(0)
+
+ self.exe = Path(sys.executable)
+ else:
+ self.exe = python_path
+
+ logging.info(f"[DEPLOY] Using Python at {str(self.exe)}")
@property
def exe(self):
def is_installed(self, package):
return bool(util.find_spec(package))
- def create_executable(self, source_file: Path, extra_args: str, config):
- if config.qml_files:
- logging.info(f"[DEPLOY] Included QML files: {config.qml_files}")
-
- command_str = self.nuitka.create_executable(source_file=source_file,
- extra_args=extra_args,
- qml_files=config.qml_files,
- excluded_qml_plugins=(config.
- excluded_qml_plugins),
- icon=config.icon,
- dry_run=self.dry_run)
-
- return command_str
+ def install_dependencies(self, config: Config, packages: str, is_android: bool = False):
+ """
+ Installs the python package dependencies for the target deployment platform
+ """
+ packages = config.get_value("python", packages).split(",")
+ if not self.init:
+ # install packages needed for deployment
+ logging.info("[DEPLOY] Installing dependencies")
+ self.install(packages=packages)
+ # nuitka requires patchelf to make patchelf rpath changes for some Qt files
+ if sys.platform.startswith("linux") and not is_android:
+ self.install(packages=["patchelf"])
+ elif is_android:
+ # install only buildozer
+ logging.info("[DEPLOY] Installing buildozer")
+ buildozer_package_with_version = ([package for package in packages
+ if package.startswith("buildozer")])
+ self.install(packages=list(buildozer_package_with_version))
AstDecorator = Union[ast.Name, ast.Call]
+AstPySideTypeSpec = Union[ast.Name, ast.Constant]
ClassList = List[dict]
return (None, None)
+def _parse_pyside_type(type_spec: AstPySideTypeSpec) -> str:
+ """Parse type specification of a Slot/Property decorator. Usually a type,
+ but can also be a string constant with a C++ type name."""
+ if isinstance(type_spec, ast.Constant):
+ return type_spec.value
+ return _python_to_cpp_type(_name(type_spec))
+
+
def _parse_call_args(call: ast.Call):
"""Parse arguments of a Signal call/Slot decorator (type list)."""
result: Arguments = []
for n, arg in enumerate(call.args):
par_name = f"a{n+1}"
- par_type = _python_to_cpp_type(_name(arg))
+ par_type = _parse_pyside_type(arg)
result.append({"name": par_name, "type": par_type})
return result
elif name == "QmlNamedElement" and node.args:
name = node.args[0].value
class_decorators.append(_decorator("QML.Element", name))
- else:
+ elif name.startswith('Q'):
print('Unknown decorator with parameters:', name,
file=sys.stderr)
return
class_decorators.append(_decorator("QML.Singleton", "true"))
elif name == "QmlAnonymous":
class_decorators.append(_decorator("QML.Element", "anonymous"))
- else:
+ elif name.startswith('Q'):
print('Unknown decorator:', name, file=sys.stderr)
return
if isinstance(node, ast.Call):
name = _name(node.func)
if name == "Property": # Property getter
- if node.args: # 1st is type
- type = _python_to_cpp_type(_name(node.args[0]))
+ if node.args: # 1st is type/type string
+ type = _parse_pyside_type(node.args[0])
prop = self._create_property_entry(func_name, type,
func_name)
_parse_property_kwargs(node.keywords, prop)
from project import (QmlProjectData, check_qml_decorators, is_python_file,
QMLDIR_FILE, MOD_CMD, METATYPES_JSON_SUFFIX,
+ SHADER_SUFFIXES, TRANSLATION_SUFFIX,
requires_rebuild, run_command, remove_path,
ProjectData, resolve_project_file, new_project,
- ProjectType)
+ ProjectType, ClOptions)
MODE_HELP = """build Builds the project
run Builds the project and runs the first file")
clean Cleans the build artifacts")
qmllint Runs the qmllint tool
deploy Deploys the application
+lupdate Updates translation (.ts) files
new-ui Creates a new QtWidgets project with a Qt Designer-based main window
new-widget Creates a new QtWidgets project with a main window
new-quick Creates a new QtQuick project
UIC_CMD = "pyside6-uic"
RCC_CMD = "pyside6-rcc"
+LRELEASE_CMD = "pyside6-lrelease"
+LUPDATE_CMD = "pyside6-lupdate"
QMLTYPEREGISTRAR_CMD = "pyside6-qmltyperegistrar"
QMLLINT_CMD = "pyside6-qmllint"
+QSB_CMD = "pyside6-qsb"
DEPLOY_CMD = "pyside6-deploy"
NEW_PROJECT_TYPES = {"new-quick": ProjectType.QUICK,
"new-widget": ProjectType.WIDGET}
+def _sort_sources(files: List[Path]) -> List[Path]:
+ """Sort the sources for building, ensure .qrc is last since it might depend
+ on generated files."""
+
+ def key_func(p: Path):
+ return p.suffix if p.suffix != ".qrc" else ".zzzz"
+
+ return sorted(files, key=key_func)
+
+
class Project:
"""
Class to wrap the various operations on Project
"""
def __init__(self, project_file: Path):
self.project = ProjectData(project_file=project_file)
+ self.cl_options = ClOptions()
# Files for QML modules using the QmlElement decorators
self._qml_module_sources: List[Path] = []
"""Run a pre-check on Python source files and find the ones with QML
decorators (representing a QML module)."""
# Quick check for any QML files (to avoid running moc for no reason).
- if not opt_qml_module and not self.project.qml_files:
+ if not self.cl_options.qml_module and not self.project.qml_files:
return
for file in self.project.files:
if is_python_file(file):
print(self._qml_module_dir)
self._qml_dir_file = self._qml_module_dir / QMLDIR_FILE
- if not opt_quiet:
+ if not self.cl_options.quiet:
count = len(self._qml_module_sources)
print(f"{self.project.project_file.name}, {count} QML file(s),"
f" {self._qml_project_data}")
cmd.extend(self._qml_project_data.registrar_options())
return ([qmltypes_file, cpp_file], cmd)
+ if file.name.endswith(TRANSLATION_SUFFIX):
+ qm_file = f"{file.parent}/{file.stem}.qm"
+ cmd = [LRELEASE_CMD, os.fspath(file), "-qm", qm_file]
+ return ([Path(qm_file)], cmd)
+
+ if file.suffix in SHADER_SUFFIXES:
+ qsb_file = f"{file.parent}/{file.stem}.qsb"
+ cmd = [QSB_CMD, "-o", qsb_file, os.fspath(file)]
+ return ([Path(qsb_file)], cmd)
+
return ([], None)
def _regenerate_qmldir(self):
"""Regenerate the 'qmldir' file."""
- if opt_dry_run or not self._qml_dir_file:
+ if self.cl_options.dry_run or not self._qml_dir_file:
return
- if opt_force or requires_rebuild(self._qml_module_sources, self._qml_dir_file):
+ if self.cl_options.force or requires_rebuild(self._qml_module_sources, self._qml_dir_file):
with self._qml_dir_file.open("w") as qf:
qf.write(f"module {self._qml_project_data.import_name}\n")
for f in self._qml_module_dir.glob("*.qmltypes"):
"""Build an artifact."""
artifacts, command = self._get_artifacts(source)
for artifact in artifacts:
- if opt_force or requires_rebuild([source], artifact):
+ if self.cl_options.force or requires_rebuild([source], artifact):
run_command(command, cwd=self.project.project_file.parent)
self._build_file(artifact) # Recurse for QML (json->qmltypes)
Project(project_file=sub_project_file).build()
if self._qml_module_dir:
self._qml_module_dir.mkdir(exist_ok=True, parents=True)
- for file in self.project.files:
+ for file in _sort_sources(self.project.files):
self._build_file(file)
self._regenerate_qmldir()
cmd.extend([str(self.project.main_file), "-f"])
run_command(cmd, cwd=self.project.project_file.parent)
+ def lupdate(self):
+ for sub_project_file in self.project.sub_projects_files:
+ Project(project_file=sub_project_file).lupdate()
+
+ if not self.project.ts_files:
+ print(f"{self.project.project_file.name}: No .ts file found.",
+ file=sys.stderr)
+ return
+
+ source_files = self.project.python_files + self.project.ui_files
+ cmd_prefix = [LUPDATE_CMD] + [p.name for p in source_files]
+ cmd_prefix.append("-ts")
+ for ts_file in self.project.ts_files:
+ if requires_rebuild(source_files, ts_file):
+ cmd = cmd_prefix
+ cmd.append(ts_file.name)
+ run_command(cmd, cwd=self.project.project_file.parent)
+
if __name__ == "__main__":
parser = ArgumentParser(description=__doc__, formatter_class=RawTextHelpFormatter)
parser.add_argument("--force", "-f", action="store_true", help="Force rebuild")
parser.add_argument("--qml-module", "-Q", action="store_true",
help="Perform check for QML module")
- mode_choices = ["build", "run", "clean", "qmllint", "deploy"]
+ mode_choices = ["build", "run", "clean", "qmllint", "deploy", "lupdate"]
mode_choices.extend(NEW_PROJECT_TYPES.keys())
parser.add_argument("mode", choices=mode_choices, default="build",
type=str, help=MODE_HELP)
parser.add_argument("file", help="Project file", nargs="?", type=str)
options = parser.parse_args()
- opt_quiet = options.quiet
- opt_dry_run = options.dry_run
- opt_force = options.force
- opt_qml_module = options.qml_module
+ cl_options = ClOptions(dry_run=options.dry_run, quiet=options.quiet, force=options.force,
+ qml_module=options.qml_module)
+
mode = options.mode
new_project_type = NEW_PROJECT_TYPES.get(mode)
project.qmllint()
elif mode == "deploy":
project.deploy()
+ elif mode == "lupdate":
+ project.lupdate()
else:
print(f"Invalid mode {mode}", file=sys.stderr)
sys.exit(1)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-opt_quiet = False
-opt_dry_run = False
-opt_force = False
-opt_qml_module = False
+from dataclasses import dataclass
QTPATHS_CMD = "qtpaths6"
MOD_CMD = "pyside6-metaobjectdump"
QT_MODULES = "QT_MODULES"
METATYPES_JSON_SUFFIX = "metatypes.json"
+TRANSLATION_SUFFIX = ".ts"
+SHADER_SUFFIXES = ".vert", ".frag"
+
+
+class Singleton(type):
+ _instances = {}
+
+ def __call__(cls, *args, **kwargs):
+ if cls not in cls._instances:
+ cls._instances[cls] = super().__call__(*args, **kwargs)
+ return cls._instances[cls]
+
+
+@dataclass(frozen=True)
+class ClOptions(metaclass=Singleton):
+ """
+ Dataclass to store the cl options that needs to be passed as arguments.
+ """
+ dry_run: bool
+ quiet: bool
+ force: bool
+ qml_module: bool
+
from .utils import (run_command, requires_rebuild, remove_path, package_dir, qtpaths,
qt_metatype_json_dir, resolve_project_file)
import sys
from typing import List, Tuple
from pathlib import Path
-from . import (METATYPES_JSON_SUFFIX, PROJECT_FILE_SUFFIX, qt_metatype_json_dir,
- MOD_CMD, QML_IMPORT_MAJOR_VERSION, QML_IMPORT_MINOR_VERSION, QML_IMPORT_NAME,
- QT_MODULES)
+from . import (METATYPES_JSON_SUFFIX, PROJECT_FILE_SUFFIX, TRANSLATION_SUFFIX,
+ qt_metatype_json_dir, MOD_CMD, QML_IMPORT_MAJOR_VERSION,
+ QML_IMPORT_MINOR_VERSION, QML_IMPORT_NAME, QT_MODULES)
def is_python_file(file: Path) -> bool:
self._ui_files: List[Path] = []
# qrc files
self._qrc_files: List[Path] = []
+ # ts files
+ self._ts_files: List[Path] = []
with project_file.open("r") as pyf:
pyproject = json.load(pyf)
self._ui_files.append(file)
elif file.suffix == ".qrc":
self._qrc_files.append(file)
+ elif file.suffix == TRANSLATION_SUFFIX:
+ self._ts_files.append(file)
if not self.main_file:
self._find_main_file()
def qml_files(self):
return self._qml_files
+ @property
+ def ts_files(self):
+ return self._ts_files
+
@property
def sub_projects_files(self):
return self._sub_projects_files
from pathlib import Path
from typing import List, Dict, Optional
-from . import opt_dry_run, opt_quiet, QTPATHS_CMD, PROJECT_FILE_SUFFIX
+from . import QTPATHS_CMD, PROJECT_FILE_SUFFIX, ClOptions
def run_command(command: List[str], cwd: str = None, ignore_fail: bool = False):
"""Run a command observing quiet/dry run"""
- if not opt_quiet or opt_dry_run:
+ cloptions = ClOptions()
+ if not cloptions.quiet or cloptions.dry_run:
print(" ".join(command))
- if not opt_dry_run:
+ if not cloptions.dry_run:
ex = subprocess.call(command, cwd=cwd)
if ex != 0 and not ignore_fail:
sys.exit(ex)
def remove_path(path: Path):
"""Remove path (file or directory) observing opt_dry_run."""
+ cloptions = ClOptions()
if not path.exists():
return
- if not opt_quiet:
+ if not cloptions.quiet:
print(f"Removing {path.name}...")
- if opt_dry_run:
+ if cloptions.dry_run:
return
_remove_path_recursion(path)
pyside_script_wrapper("android_deploy.py")
+def qsb():
+ qt_tool_wrapper("qsb", sys.argv[1:])
+
+
+def balsam():
+ qt_tool_wrapper("balsam", sys.argv[1:])
+
+
+def balsamui():
+ qt_tool_wrapper("balsamui", sys.argv[1:])
+
+
if __name__ == "__main__":
main()
from PySide6.QtCore import QCoreApplication, Qt, QLibraryInfo, QUrl, SignalInstance
from PySide6.QtGui import QGuiApplication, QSurfaceFormat
from PySide6.QtQml import QQmlApplicationEngine, QQmlComponent
-from PySide6.QtQuick import QQuickView, QQuickWindow
+from PySide6.QtQuick import QQuickView, QQuickItem
from PySide6.QtWidgets import QApplication
sys.exit(-1)
qquick_view = False
- if isinstance(rootObjects[0], QQuickWindow) and qquick_present:
+ if isinstance(rootObjects[0], QQuickItem) and qquick_present:
logging.info("qml: loading with QQuickView")
viewer = QQuickView()
viewer.setSource(qml_file)
set(pyside_MAJOR_VERSION "6")
-set(pyside_MINOR_VERSION "6")
+set(pyside_MINOR_VERSION "7")
set(pyside_MICRO_VERSION "2")
set(pyside_PRE_RELEASE_VERSION_TYPE "")
set(pyside_PRE_RELEASE_VERSION "")
${Qt3DAnimation_GEN_DIR}/qt3danimation_qanimationcontroller_wrapper.cpp
${Qt3DAnimation_GEN_DIR}/qt3danimation_qanimationgroup_wrapper.cpp
${Qt3DAnimation_GEN_DIR}/qt3danimation_qblendedclipanimator_wrapper.cpp
+${Qt3DAnimation_GEN_DIR}/qt3danimation_qcallbackmapping_wrapper.cpp
${Qt3DAnimation_GEN_DIR}/qt3danimation_qchannel_wrapper.cpp
${Qt3DAnimation_GEN_DIR}/qt3danimation_qchannelcomponent_wrapper.cpp
${Qt3DAnimation_GEN_DIR}/qt3danimation_qchannelmapper_wrapper.cpp
${pyside6_SOURCE_DIR}
${Qt${QT_MAJOR_VERSION}Core_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Gui_INCLUDE_DIR}
+ ${Qt${QT_MAJOR_VERSION}OpenGL_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}3DCore_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}3DRender_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}3DAnimation_INCLUDE_DIRS}
${libpyside_SOURCE_DIR}
${QtCore_GEN_DIR}
${QtGui_GEN_DIR}
+ ${QtOpenGL_GEN_DIR}
${Qt3DCore_GEN_DIR}
${Qt3DRender_GEN_DIR}
${Qt3DAnimation_GEN_DIR})
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.Qt3DAnimation">
+<typesystem package="PySide6.Qt3DAnimation"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="Qt3DRender/typesystem_3drender.xml" generate="no"/>
<namespace-type name="Qt3DAnimation">
<object-type name="QAbstractAnimation">
<object-type name="QBlendedClipAnimator"/>
<value-type name="QChannel"/>
<value-type name="QChannelComponent"/>
+ <object-type name="QCallbackMapping"/>
<object-type name="QChannelMapper" since="6.1"/>
<object-type name="QChannelMapping"/>
<object-type name="QClipAnimator"/>
${Qt3DCore_GEN_DIR}/qt3dcore_qboundingvolume_wrapper.cpp
${Qt3DCore_GEN_DIR}/qt3dcore_qbuffer_wrapper.cpp
${Qt3DCore_GEN_DIR}/qt3dcore_qcomponent_wrapper.cpp
+${Qt3DCore_GEN_DIR}/qt3dcore_qcoreaspect_wrapper.cpp
${Qt3DCore_GEN_DIR}/qt3dcore_qcoresettings_wrapper.cpp
${Qt3DCore_GEN_DIR}/qt3dcore_qentity_wrapper.cpp
${Qt3DCore_GEN_DIR}/qt3dcore_qgeometry_wrapper.cpp
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.Qt3DCore">
+<typesystem package="PySide6.Qt3DCore"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/>
<smart-pointer-type name="QSharedPointer" type="shared" getter="data"
reset-method="reset"
</modify-argument>
</modify-function>
</object-type>
+ <object-type name="QCoreAspect"/>
<object-type name="QCoreSettings"/>
<object-type name="QGeometry"/>
<object-type name="QGeometryView">
${pyside6_SOURCE_DIR}
${Qt${QT_MAJOR_VERSION}Core_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Gui_INCLUDE_DIRS}
+ ${Qt${QT_MAJOR_VERSION}OpenGL_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}3DCore_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}3DRender_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}3DExtras_INCLUDE_DIRS}
${libpyside_SOURCE_DIR}
${QtCore_GEN_DIR}
${QtGui_GEN_DIR}
+ ${QtOpenGL_GEN_DIR}
${Qt3DCore_GEN_DIR}
${Qt3DRender_GEN_DIR})
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.Qt3DExtras">
+<typesystem package="PySide6.Qt3DExtras"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="Qt3DRender/typesystem_3drender.xml" generate="no"/>
<namespace-type name="Qt3DExtras">
<object-type name="QAbstractCameraController">
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.Qt3DInput">
+<typesystem package="PySide6.Qt3DInput"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="Qt3DCore/typesystem_3dcore.xml" generate="no"/>
<namespace-type name="Qt3DInput">
<object-type name="QAbstractActionInput"/>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.Qt3DLogic">
+<typesystem package="PySide6.Qt3DLogic"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="Qt3DCore/typesystem_3dcore.xml" generate="no"/>
<namespace-type name="Qt3DLogic">
<object-type name="QFrameAction"/>
project(Qt3DRender)
+set(Qt3DRender_DROPPED_ENTRIES)
+
set(Qt3DRender_SRC
${Qt3DRender_GEN_DIR}/qsharedpointer_propertyreaderinterface_wrapper.cpp
${Qt3DRender_GEN_DIR}/qsharedpointer_qtextureimagedata_wrapper.cpp
${Qt3DRender_GEN_DIR}/qt3drender_qcolormask_wrapper.cpp
${Qt3DRender_GEN_DIR}/qt3drender_qcomputecommand_wrapper.cpp
${Qt3DRender_GEN_DIR}/qt3drender_qcullface_wrapper.cpp
+${Qt3DRender_GEN_DIR}/qt3drender_qdebugoverlay_wrapper.cpp
${Qt3DRender_GEN_DIR}/qt3drender_qdepthrange_wrapper.cpp
${Qt3DRender_GEN_DIR}/qt3drender_qdepthtest_wrapper.cpp
${Qt3DRender_GEN_DIR}/qt3drender_qdirectionallight_wrapper.cpp
${libpyside_SOURCE_DIR}
${QtCore_GEN_DIR}
${QtGui_GEN_DIR}
- ${QtOpenGL_GEN_DIR}
+
${Qt3DCore_GEN_DIR})
set(Qt3DRender_libraries pyside6
${Qt${QT_MAJOR_VERSION}3DRender_LIBRARIES})
-set(Qt3DRender_deps Qt3DCore QtOpenGL)
+set(Qt3DRender_deps Qt3DCore)
+
+check_qt_opengl("3DRender" Qt3DRender_include_dirs Qt3DRender_deps
+ Qt3DRender_DROPPED_ENTRIES)
create_pyside_module(NAME Qt3DRender
INCLUDE_DIRS Qt3DRender_include_dirs
DEPS Qt3DRender_deps
TYPESYSTEM_PATH Qt3DRender_SOURCE_DIR
SOURCES Qt3DRender_SRC
- TYPESYSTEM_NAME ${Qt3DRender_BINARY_DIR}/typesystem_3drender.xml)
+ TYPESYSTEM_NAME ${Qt3DRender_BINARY_DIR}/typesystem_3drender.xml
+ DROPPED_ENTRIES Qt3DRender_DROPPED_ENTRIES)
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.Qt3DRender">
+<typesystem package="PySide6.Qt3DRender"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="Qt3DCore/typesystem_3dcore.xml" generate="no"/>
+ <load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/>
+ <!-- PYSIDE-2610: QOpenGLTexture's enums are used -->
+ <?if !no_QtOpenGL?>
+ <load-typesystem name="QtOpenGL/typesystem_opengl.xml" generate="no"/>
+ <?endif?>
<smart-pointer-type name="QSharedPointer" type="shared" getter="data"
reset-method="reset"
instantiations="Qt3DRender::PropertyReaderInterface=Qt3DRender::PropertyReaderInterfacePtr,Qt3DRender::QTextureImageData=Qt3DRender::QTextureImageDataPtr,Qt3DRender::QTextureImageDataGenerator=Qt3DRender::QTextureImageDataGeneratorPtr"/>
<enum-type name="Target"/>
<enum-type name="TextureFormat"/>
</object-type>
- <object-type name="QAbstractTextureImage">
- <modify-function signature="QAbstractTextureImage(Qt3DCore::QNode*)" remove="all"/>
- </object-type>
+ <object-type name="QAbstractTextureImage"/>
<object-type name="QAlphaCoverage"/>
<object-type name="QAlphaTest">
<enum-type name="AlphaFunction"/>
<object-type name="QCullFace">
<enum-type name="CullingMode"/>
</object-type>
+ <object-type name="QDebugOverlay"/>
<object-type name="QDepthRange"/>
<object-type name="QDepthTest">
<enum-type name="DepthFunction"/>
<enum-type name="Status"/>
</object-type>
<object-type name="QTextureImageData"/>
- <object-type name="QTextureImageDataGenerator">
- <modify-function signature="QTextureImageDataGenerator()" remove="all"/>
- </object-type>
+ <object-type name="QTextureImageDataGenerator"/>
<object-type name="QTextureLoader"/>
<object-type name="QTextureRectangle"/>
<object-type name="QTextureWrapMode">
def run(coro: typing.Optional[typing.Coroutine] = None,
keep_running: bool = True,
quit_qapp: bool = True, *,
- debug: typing.Optional[bool] = None) -> None:
+ handle_sigint: bool = False,
+ debug: typing.Optional[bool] = None) -> typing.Any:
"""Run the QtAsyncio event loop."""
# Event loop policies are expected to be deprecated with Python 3.13, with
# subsequent removal in Python 3.15. At that point, part of the current
# logic of the QAsyncioEventLoopPolicy constructor will have to be moved
# here and/or to a loop factory class (to be provided as an argument to
- # asyncio.run()), namely setting up the QCoreApplication and the SIGINT
- # handler.
+ # asyncio.run()). In particular, this concerns the logic of setting up the
+ # QCoreApplication and the SIGINT handler.
#
# More details:
# https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553 # noqa: E501
- asyncio.set_event_loop_policy(QAsyncioEventLoopPolicy(quit_qapp=quit_qapp))
+ default_policy = asyncio.get_event_loop_policy()
+ asyncio.set_event_loop_policy(
+ QAsyncioEventLoopPolicy(quit_qapp=quit_qapp, handle_sigint=handle_sigint))
+
+ ret = None
+ exc = None
if keep_running:
if coro:
asyncio.get_event_loop().run_forever()
else:
if coro:
- asyncio.run(coro, debug=debug)
+ ret = asyncio.run(coro, debug=debug)
else:
- raise RuntimeError(
+ exc = RuntimeError(
"QtAsyncio was set to keep running after the coroutine "
"finished, but no coroutine was provided.")
+
+ asyncio.set_event_loop_policy(default_policy)
+
+ if ret:
+ return ret
+ if exc:
+ raise exc
class QAsyncioExecutorWrapper(QObject):
+ """
+ Executors in asyncio allow running synchronous code in a separate thread or
+ process without blocking the event loop or interrupting the asynchronous
+ program flow. Callables are scheduled for execution by calling submit() or
+ map() on an executor object.
+
+ Executors require a bit of extra work for QtAsyncio, as we can't use
+ naked Python threads; instead, we must make sure that the thread created
+ by executor.submit() has an event loop. This is achieved by not submitting
+ the callable directly, but a small wrapper that attaches a QEventLoop to
+ the executor thread, and then creates a zero-delay singleshot timer to push
+ the actual callable for the executor into this new event loop.
+ """
def __init__(self, func: typing.Callable, *args: typing.Tuple) -> None:
super().__init__()
def _cb(self):
try:
+ # Call the synchronous callable that we submitted with submit() or
+ # map().
self._result = self._func(*self._args)
except BaseException as e:
self._exception = e
self._loop.exit()
def do(self):
- # This creates a new event loop and dispatcher for the thread, if not already created.
+ # This creates a new event loop and dispatcher for the thread, if not
+ # already created.
self._loop = QEventLoop()
asyncio.events._set_running_loop(self._loop)
+
QTimer.singleShot(0, self._loop, lambda: self._cb())
+
self._loop.exec()
if self._exception is not None:
raise self._exception
class QAsyncioEventLoopPolicy(asyncio.AbstractEventLoopPolicy):
+ """
+ Event loop policies are expected to be deprecated with Python 3.13, with
+ subsequent removal in Python 3.15. At that point, part of the current
+ logic of the QAsyncioEventLoopPolicy constructor will have to be moved
+ to QtAsyncio.run() and/or to a loop factory class (to be provided as an
+ argument to asyncio.run()). In particular, this concerns the logic of
+ setting up the QCoreApplication and the SIGINT handler.
+
+ More details:
+ https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553
+ """
def __init__(self,
application: typing.Optional[QCoreApplication] = None,
- quit_qapp: bool = True) -> None:
+ quit_qapp: bool = True,
+ handle_sigint: bool = False) -> None:
super().__init__()
if application is None:
if QCoreApplication.instance() is None:
else:
application = QCoreApplication.instance()
self._application: QCoreApplication = application # type: ignore[assignment]
+
+ # Configure whether the QCoreApplication at the core of QtAsyncio
+ # should be shut down when asyncio finishes. A special case where one
+ # would want to disable this is test suites that want to reuse a single
+ # QCoreApplication instance across all unit tests, which would fail if
+ # this instance is shut down every time.
self._quit_qapp = quit_qapp
+
self._event_loop: typing.Optional[asyncio.AbstractEventLoop] = None
- signal.signal(signal.SIGINT, signal.SIG_DFL)
+ if handle_sigint:
+ signal.signal(signal.SIGINT, signal.SIG_DFL)
def get_event_loop(self) -> asyncio.AbstractEventLoop:
if self._event_loop is None:
- self._event_loop = QAsyncioEventLoop(self._application)
+ self._event_loop = QAsyncioEventLoop(self._application, quit_qapp=self._quit_qapp)
return self._event_loop
def set_event_loop(self, loop: typing.Optional[asyncio.AbstractEventLoop]) -> None:
def new_event_loop(self) -> asyncio.AbstractEventLoop:
return QAsyncioEventLoop(self._application, quit_qapp=self._quit_qapp)
- def get_child_watcher(self) -> asyncio.AbstractChildWatcher:
+ def get_child_watcher(self) -> "asyncio.AbstractChildWatcher":
raise DeprecationWarning("Child watchers are deprecated since Python 3.12")
- def set_child_watcher(self, watcher: asyncio.AbstractChildWatcher) -> None:
+ def set_child_watcher(self, watcher: "asyncio.AbstractChildWatcher") -> None:
raise DeprecationWarning("Child watchers are deprecated since Python 3.12")
"""
class ShutDownThread(QThread):
+ """
+ Used to shut down the default executor when calling
+ shutdown_default_executor(). As the executor is a ThreadPoolExecutor,
+ it must be shut down in a separate thread as all the threads from the
+ thread pool must join, which we want to do without blocking the event
+ loop.
+ """
+
def __init__(self, future: futures.QAsyncioFuture, loop: "QAsyncioEventLoop") -> None:
super().__init__()
self._future = future
QObject.__init__(self)
self._application: QCoreApplication = application
+
+ # Configure whether the QCoreApplication at the core of QtAsyncio
+ # should be shut down when asyncio finishes. A special case where one
+ # would want to disable this is test suites that want to reuse a single
+ # QCoreApplication instance across all unit tests, which would fail if
+ # this instance is shut down every time.
self._quit_qapp = quit_qapp
+
self._thread = QThread.currentThread()
self._closed = False
+ # These two flags are used to determine whether the loop was stopped
+ # from inside the loop (i.e., coroutine or callback called stop()) or
+ # from outside the loop (i.e., the QApplication is being shut down, for
+ # example, by the user closing the window or by calling
+ # QApplication.quit()). The different cases can trigger slightly
+ # different behaviors (see the comments where the flags are used).
+ # There are two variables for this as in a third case the loop is still
+ # running and both flags are False.
self._quit_from_inside = False
self._quit_from_outside = False
+ # A set of all asynchronous generators that are currently running.
self._asyncgens: typing.Set[collections.abc.AsyncGenerator] = set()
# Starting with Python 3.11, this must be an instance of
# ThreadPoolExecutor.
self._default_executor = concurrent.futures.ThreadPoolExecutor()
+ # The exception handler, if set with set_exception_handler(). The
+ # exception handler is currently called in two places: One, if an
+ # asynchonrous generator raises an exception when closed, and two, if
+ # an exception is raised during the execution of a task. Currently, the
+ # default exception handler just prints the exception to the console.
self._exception_handler: typing.Optional[typing.Callable] = self.default_exception_handler
+
+ # The task factory, if set with set_task_factory(). Otherwise, a new
+ # task is created with the QAsyncioTask constructor.
self._task_factory: typing.Optional[typing.Callable] = None
+
+ # The future that is currently being awaited with run_until_complete().
self._future_to_complete: typing.Optional[futures.QAsyncioFuture] = None
self._debug = bool(os.getenv("PYTHONASYNCIODEBUG", False))
# Running and stopping the loop
def _run_until_complete_cb(self, future: futures.QAsyncioFuture) -> None:
+ """
+ A callback that stops the loop when the future is done, used when
+ running the loop with run_until_complete().
+ """
if not future.cancelled():
if isinstance(future.exception(), (SystemExit, KeyboardInterrupt)):
return
asyncio.events._set_running_loop(None)
def _about_to_quit_cb(self):
+ """ A callback for the aboutToQuit signal of the QCoreApplication. """
if not self._quit_from_inside:
+ # If the aboutToQuit signal is emitted, the user is closing the
+ # application window or calling QApplication.quit(). In this case,
+ # we want to close the event loop, and we consider this a quit from
+ # outside the loop.
self._quit_from_outside = True
+ self.close()
def stop(self) -> None:
if self._future_to_complete is not None:
if self._future_to_complete.done():
self._future_to_complete = None
else:
+ # Do not stop the loop if there is a future still being awaited
+ # with run_until_complete().
return
+
self._quit_from_inside = True
+
+ # The user might want to keep the QApplication running after the event
+ # event loop finishes, which they can control with the quit_qapp
+ # argument.
if self._quit_qapp:
self._application.quit()
return self._closed
def close(self) -> None:
- if self.is_running():
+ if self.is_running() and not self._quit_from_outside:
raise RuntimeError("Cannot close a running event loop")
if self.is_closed():
return
if self._default_executor is not None:
self._default_executor.shutdown(wait=False)
- if self._quit_qapp:
- self._application.shutdown()
self._closed = True
async def shutdown_asyncgens(self) -> None:
def call_soon_threadsafe(self, callback: typing.Callable, *args: typing.Any,
context:
typing.Optional[contextvars.Context] = None) -> asyncio.Handle:
+ if self.is_closed():
+ raise RuntimeError("Event loop is closed")
if context is None:
context = contextvars.copy_context()
return self._call_soon_impl(callback, *args, context=context, is_threadsafe=True)
callback: typing.Callable, *args: typing.Any,
context: typing.Optional[contextvars.Context] = None,
is_threadsafe: typing.Optional[bool] = False) -> asyncio.TimerHandle:
+ """ All call_at() and call_later() methods map to this method. """
if not isinstance(when, (int, float)):
raise TypeError("when must be an int or float")
- if self.is_closed():
- raise RuntimeError("Event loop is closed")
return QAsyncioTimerHandle(when, callback, args, self, context, is_threadsafe=is_threadsafe)
def call_at(self, when: typing.Union[int, float],
coro: typing.Union[collections.abc.Generator, collections.abc.Coroutine],
*, name: typing.Optional[str] = None,
context: typing.Optional[contextvars.Context] = None) -> tasks.QAsyncioTask:
- if self.is_closed():
- raise RuntimeError("Event loop is closed")
if self._task_factory is None:
task = tasks.QAsyncioTask(coro, loop=self, name=name, context=context)
else:
raise RuntimeError("Event loop is closed")
if executor is None:
executor = self._default_executor
+
+ # Executors require a bit of extra work for QtAsyncio, as we can't use
+ # naked Python threads; instead, we must make sure that the thread
+ # created by executor.submit() has an event loop. This is achieved by
+ # not submitting the callable directly, but a small wrapper that
+ # attaches a QEventLoop to the executor thread, and then pushes the
+ # actual callable for the executor into this new event loop.
wrapper = QAsyncioExecutorWrapper(func, *args)
return asyncio.futures.wrap_future(
executor.submit(wrapper.do), loop=self
class QAsyncioHandle():
+ """
+ The handle enqueues a callback to be executed by the event loop, and allows
+ for this callback to be cancelled before it is executed. This callback will
+ typically execute the step function for a task. This makes the handle one
+ of the main components of asyncio.
+ """
class HandleState(enum.Enum):
PENDING = enum.auto()
CANCELLED = enum.auto()
self._state = QAsyncioHandle.HandleState.PENDING
self._start()
+ def _start(self) -> None:
+ self._schedule_event(self._timeout, lambda: self._cb())
+
def _schedule_event(self, timeout: int, func: typing.Callable) -> None:
+ # Do not schedule events from asyncio when the app is quit from outside
+ # the event loop, as this would cause events to be enqueued after the
+ # event loop was destroyed.
if not self._loop.is_closed() and not self._loop._quit_from_outside:
if self._is_threadsafe:
+ # This singleShot overload will push func into self._loop
+ # instead of the current thread's loop. This allows scheduling
+ # a callback from a different thread, which is necessary for
+ # thread-safety.
+ # https://docs.python.org/3/library/asyncio-dev.html#asyncio-multithreading
QTimer.singleShot(timeout, self._loop, func)
else:
QTimer.singleShot(timeout, func)
- def _start(self) -> None:
- self._schedule_event(self._timeout, lambda: self._cb())
-
@Slot()
def _cb(self) -> None:
+ """
+ A slot, enqueued into the event loop, that wraps around the actual
+ callback, typically the step function of a task.
+ """
if self._state == QAsyncioHandle.HandleState.PENDING:
if self._context is not None:
self._context.run(self._callback, *self._args)
def cancel(self) -> None:
if self._state == QAsyncioHandle.HandleState.PENDING:
- # The old timer that was created in _start will still trigger but _cb won't do anything.
+ # The old timer that was created in _start will still trigger but
+ # _cb won't do anything, therefore the callback is effectively
+ # cancelled.
self._state = QAsyncioHandle.HandleState.CANCELLED
def cancelled(self) -> bool:
QAsyncioHandle.__init__(self, callback, args, loop, context, is_threadsafe)
self._when = when
- self._timeout = int(max(self._when - self._loop.time(), 0) * 1000)
+ time = self._loop.time()
+ self._timeout = round(max(self._when - time, 0) * 1000)
QAsyncioHandle._start(self)
- # Override this so that timer.start() is only called once at the end
- # of the constructor for both QtHandle and QtTimerHandle.
def _start(self) -> None:
+ """
+ Overridden so that timer.start() is only called once at the end of the
+ constructor for both QtHandle and QtTimerHandle.
+ """
pass
def when(self) -> float:
self._result: typing.Any = None
self._exception: typing.Optional[BaseException] = None
- self._callbacks: typing.List[typing.Callable] = list()
-
self._cancel_message: typing.Optional[str] = None
+ # List of callbacks that are called when the future is done.
+ self._callbacks: typing.List[typing.Callable] = list()
+
def __await__(self):
if not self.done():
self._asyncio_future_blocking = True
__iter__ = __await__
def _schedule_callbacks(self, context: typing.Optional[contextvars.Context] = None):
+ """ A future can optionally have callbacks that are called when the future is done. """
for cb in self._callbacks:
self._loop.call_soon(
cb, self, context=context if context else self._context)
context: typing.Optional[contextvars.Context] = None) -> None:
super().__init__(loop=loop, context=context)
- self._coro = coro
+ self._coro = coro # The coroutine for which this task was created.
self._name = name if name else "QtTask"
+ # The task creates a handle for its coroutine. The handle enqueues the
+ # task's step function as its callback in the event loop.
self._handle = self._loop.call_soon(self._step, context=self._context)
- self._cancellation_requests = 0
-
+ # The task step function executes the coroutine until it finishes,
+ # raises an exception or returns a future. If a future was returned,
+ # the task will await its completion (or exception).
self._future_to_await: typing.Optional[asyncio.Future] = None
+
+ self._cancelled = False
self._cancel_message: typing.Optional[str] = None
+ # https://docs.python.org/3/library/asyncio-extending.html#task-lifetime-support
asyncio._register_task(self) # type: ignore[arg-type]
def __repr__(self) -> str:
def _step(self,
exception_or_future: typing.Union[
BaseException, futures.QAsyncioFuture, None] = None) -> None:
+ """
+ The step function is the heart of a task. It is scheduled in the event
+ loop repeatedly, executing the coroutine "step" by "step" (i.e.,
+ iterating through the asynchronous generator) until it finishes with an
+ exception or successfully. Each step can optionally receive an
+ exception or a future as a result from a previous step to handle.
+ """
+
if self.done():
return
result = None
self._future_to_await = None
+ if asyncio.futures.isfuture(exception_or_future):
+ try:
+ exception_or_future.result()
+ except BaseException as e:
+ exception_or_future = e
+
try:
asyncio._enter_task(self._loop, self) # type: ignore[arg-type]
- if exception_or_future is None:
- result = self._coro.send(None)
- elif asyncio.futures.isfuture(exception_or_future):
- try:
- exception_or_future.result()
- except BaseException as e:
- result = self._coro.throw(e)
- else:
- result = self._coro.send(None)
- elif isinstance(exception_or_future, BaseException):
+
+ # It is at this point that the coroutine is resumed for the current
+ # step (i.e. asynchronous generator iteration). It will now be
+ # executed until it yields (and potentially returns a future),
+ # raises an exception, is cancelled, or finishes successfully.
+
+ if isinstance(exception_or_future, BaseException):
+ # If the coroutine doesn't handle this exception, it propagates
+ # to the caller.
result = self._coro.throw(exception_or_future)
+ else:
+ result = self._coro.send(None)
except StopIteration as e:
self._state = futures.QAsyncioFuture.FutureState.DONE_WITH_RESULT
self._result = e.value
self._exception = e
else:
if asyncio.futures.isfuture(result):
+ # If the coroutine yields a future, the task will await its
+ # completion, and at that point the step function will be
+ # called again.
result.add_done_callback(
self._step, context=self._context) # type: ignore[arg-type]
self._future_to_await = result
+ if self._cancelled:
+ # If the task was cancelled, then a new future should be
+ # cancelled as well. Otherwise, in some scenarios like
+ # a loop inside the task and with bad timing, if the new
+ # future is not cancelled, the task would continue running
+ # in this loop despite having been cancelled. This bad
+ # timing can occur especially if the first future finishes
+ # very quickly.
+ self._future_to_await.cancel(self._cancel_message)
elif result is None:
+ # If no future was yielded, we schedule the step function again
+ # without any arguments.
self._loop.call_soon(self._step, context=self._context)
else:
+ # This is not supposed to happen.
exception = RuntimeError(f"Bad task result: {result}")
self._loop.call_soon(self._step, exception, context=self._context)
finally:
asyncio._leave_task(self._loop, self) # type: ignore[arg-type]
+
if self._exception:
self._loop.call_exception_handler({
"message": (str(self._exception) if self._exception
if asyncio.futures.isfuture(exception_or_future)
else None)
})
+
if self.done():
self._schedule_callbacks()
+
+ # https://docs.python.org/3/library/asyncio-extending.html#task-lifetime-support
asyncio._unregister_task(self) # type: ignore[arg-type]
def get_stack(self, *, limit=None) -> typing.List[typing.Any]:
self._cancel_message = msg
self._handle.cancel()
if self._future_to_await is not None:
+ # A task that is awaiting a future must also cancel this future in
+ # order for the cancellation to be successful.
self._future_to_await.cancel(msg)
+ self._cancelled = True
return True
def uncancel(self) -> None:
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtAxContainer">
+<typesystem package="PySide6.QtAxContainer"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtWidgets/typesystem_widgets.xml" generate="no"/>
<rejection class="*" function-name="connectNotify"/>
<rejection class="*" function-name="queryInterface"/>
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtBluetooth">
+<typesystem package="PySide6.QtBluetooth"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<namespace-type name="QBluetooth">
<enum-type name="AttAccessConstraint" flags="AttAccessConstraints"/>
${QtWidgets_GEN_DIR})
set(QtCharts_libraries pyside6
- ${Qt${QT_MAJOR_VERSION}Charts_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Gui_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Widgets_LIBRARIES})
+ ${Qt${QT_MAJOR_VERSION}Charts_LIBRARIES})
set(QtCharts_deps QtCore QtGui QtWidgets)
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtCharts">
+<typesystem package="PySide6.QtCharts"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtWidgets/typesystem_widgets.xml" generate="no"/>
<!-- PYSIDE-1101 Removing inherited method to avoid argument conflict
on the QChart::scroll overload -->
${Qt${QT_MAJOR_VERSION}Core_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Concurrent_INCLUDE_DIRS}
${libpyside_SOURCE_DIR}
- ${QtCore_GEN_DIR}
- )
-set(QtConcurrent_libraries pyside6
- ${QtConcurrent_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- )
+ ${QtCore_GEN_DIR})
+
+set(QtConcurrent_libraries pyside6
+ ${Qt${QT_MAJOR_VERSION}Concurrent_LIBRARIES})
+
set(QtConcurrent_deps QtCore)
create_pyside_module(NAME QtConcurrent
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtConcurrent">
+<typesystem package="PySide6.QtConcurrent"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<!-- Qt5: this is currently the minimum possible QtConcurrent support, by just extracting
project(QtCore)
+set(CMAKE_AUTOMOC ON)
+
set(QtCore_DROPPED_ENTRIES )
set(QtCore_static_sources
"${QtCore_SOURCE_DIR}/glue/qeasingcurve_glue.cpp"
"${QtCore_SOURCE_DIR}/glue/core_snippets.cpp"
"${QtCore_SOURCE_DIR}/glue/qtcorehelper.cpp"
+ "${QtCore_SOURCE_DIR}/glue/qiopipe.cpp"
+ "${pyside6_SOURCE_DIR}/qiopipe.h"
)
if(ENABLE_WIN)
${QtCore_GEN_DIR}/qt_wrapper.cpp
${QtCore_GEN_DIR}/qtcorehelper_qgenericargumentholder_wrapper.cpp
${QtCore_GEN_DIR}/qtcorehelper_qgenericreturnargumentholder_wrapper.cpp
+${QtCore_GEN_DIR}/qtcorehelper_qiopipe_wrapper.cpp
${QtCore_GEN_DIR}/qtcorehelper_qmutexlocker_wrapper.cpp
${QtCore_GEN_DIR}/qtemporarydir_wrapper.cpp
${QtCore_GEN_DIR}/qtemporaryfile_wrapper.cpp
)
set(QtCore_libraries pyside6
${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
+ Qt::CorePrivate
)
create_pyside_module(NAME QtCore
DROPPED_ENTRIES QtCore_DROPPED_ENTRIES
)
-install(FILES ${pyside6_SOURCE_DIR}/qtcorehelper.h DESTINATION include/PySide6/QtCore/)
+# Note: The permission API for Apple platforms only works if the Python application is packaged
+# with pyside6-deploy (uses Nuitka). Read "Notes for Developers" in Qt for Python documentation
+# for more information
+#
+# For Apple platforms, the Qt permission API implementations are in small static libraries.
+# In Qt C++, the application is linked directly to these static libraries during the build when
+# linking to the QtCore module as a post processing CMake step.
+# Being static plugins makes it difficult to add these plugins during Nuitka packaging step.
+# Thus, we link the static plugins to QtCore.abi3.so. However, to request the permissions
+# it is still required to have the necessary Information Property keys eg: NSCameraUsageDescription
+# in the Info.plist of the application bundle which Nuitka creates.
+if (APPLE)
+ set(permissions Camera Microphone Bluetooth Contacts Calendar)
+ foreach(permission IN LISTS permissions)
+ set(permission_plugin_name "QDarwin${permission}PermissionPlugin")
+ set(permission_plugin "${QT_CMAKE_EXPORT_NAMESPACE}::${permission_plugin_name}")
+ # Setting this property is necessary for Camera and Microphone. Otherwise it won't append
+ # the linker flags like -Wl,-u,_QDarwinMicrophonePermissionRequest which are required to
+ # link to qdarwinpermissionplugin_microphone_request.mm.o and find symbols like
+ # QDarwinMicrosphonePermissionHandler which handles requesting the actual permission
+ set_target_properties(QtCore PROPERTIES "_qt_has_${permission_plugin_name}_usage_description" TRUE)
+ # importing the plugin
+ qt6_import_plugins(QtCore INCLUDE ${permission_plugin})
+ endforeach()
+endif()
+
+install(FILES ${pyside6_SOURCE_DIR}/qtcorehelper.h ${pyside6_SOURCE_DIR}/qiopipe.h
+ DESTINATION include/PySide6/QtCore/)
#include <qtcorehelper.h>
+#include <qiopipe.h>
a6.toGenericArgument(), a7.toGenericArgument(), a8.toGenericArgument(),
a9.toGenericArgument());
PyEval_RestoreThread(_save); // Py_END_ALLOW_THREADS
- if (!callResult) {
- PyErr_SetString(PyExc_RuntimeError, "QMetaMethod invocation failed.");
- return nullptr;
- }
+ if (!callResult)
+ return PyErr_Format(PyExc_RuntimeError, "QMetaMethod invocation failed.");
return convertGenericReturnArgument(r.data(), r.metaType());
}
QT_FORWARD_DECLARE_CLASS(QRegularExpression)
QT_FORWARD_DECLARE_CLASS(QVariant);
+QT_BEGIN_NAMESPACE
namespace QtCoreHelper {
class QGenericArgumentHolder;
class QGenericReturnArgumentHolder;
}
+QT_END_NAMESPACE
// Helpers for QVariant conversion
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qiopipe.h"
+
+#include <QtCore/private/qobject_p.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qiodevice.h>
+#include <QtCore/qpointer.h>
+
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+
+namespace QtCoreHelper
+{
+
+class QPipeEndPoint : public QIODevice
+{
+ Q_OBJECT
+
+public:
+ bool isSequential() const override;
+ qint64 bytesAvailable() const override;
+
+ void setRemoteEndPoint(QPipeEndPoint *other);
+
+protected:
+ qint64 readData(char *data, qint64 maxlen) override;
+ qint64 writeData(const char *data, qint64 len) override;
+
+private:
+ QByteArray m_buffer;
+ QPointer<QPipeEndPoint> m_remoteEndPoint;
+};
+
+class QIOPipePrivate final : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QIOPipe)
+public:
+ QIOPipePrivate();
+ ~QIOPipePrivate() {};
+
+ std::unique_ptr<QPipeEndPoint> end1;
+ std::unique_ptr<QPipeEndPoint> end2;
+};
+
+QIOPipe::QIOPipe(QObject *parent) : QObject(*(new QIOPipePrivate()), parent) { }
+
+bool QIOPipe::open(QIODevice::OpenMode mode)
+{
+ Q_D(QIOPipe);
+
+ if (!d->end1->open(mode))
+ return false;
+ switch (mode & QIODevice::ReadWrite) {
+ case QIODevice::WriteOnly:
+ case QIODevice::ReadOnly:
+ return d->end2->open(mode ^ QIODevice::ReadWrite);
+ default:
+ return d->end2->open(mode);
+ }
+}
+
+QIODevice *QIOPipe::end1() const
+{
+ Q_D(const QIOPipe);
+ return d->end1.get();
+}
+
+QIODevice *QIOPipe::end2() const
+{
+ Q_D(const QIOPipe);
+ return d->end2.get();
+}
+
+QIOPipePrivate::QIOPipePrivate() : end1(std::make_unique<QPipeEndPoint>()),
+ end2(std::make_unique<QPipeEndPoint>())
+{
+ end1->setRemoteEndPoint(end2.get());
+ end2->setRemoteEndPoint(end1.get());
+}
+
+bool QPipeEndPoint::isSequential() const
+{
+ return true;
+}
+
+qint64 QPipeEndPoint::bytesAvailable() const
+{
+ return m_buffer.size() + QIODevice::bytesAvailable();
+}
+
+void QPipeEndPoint::setRemoteEndPoint(QPipeEndPoint *other)
+{
+ m_remoteEndPoint = other;
+}
+
+qint64 QPipeEndPoint::readData(char *data, qint64 maxlen)
+{
+ maxlen = qMin(maxlen, static_cast<qint64>(m_buffer.size()));
+ if (maxlen <= 0)
+ return 0;
+
+ Q_ASSERT(maxlen > 0);
+ memcpy(data, m_buffer.data(), static_cast<size_t>(maxlen));
+ m_buffer = m_buffer.mid(maxlen);
+ return maxlen;
+}
+
+qint64 QPipeEndPoint::writeData(const char *data, qint64 len)
+{
+ if (!m_remoteEndPoint)
+ return -1;
+
+ if (len <= 0)
+ return 0;
+
+ QByteArray &buffer = m_remoteEndPoint->m_buffer;
+ const qint64 prevLen = buffer.size();
+ Q_ASSERT(prevLen >= 0);
+ len = qMin(len, std::numeric_limits<int>::max() - prevLen);
+
+ if (len == 0)
+ return 0;
+
+ Q_ASSERT(len > 0);
+ Q_ASSERT(prevLen + len > 0);
+ Q_ASSERT(prevLen + len <= std::numeric_limits<int>::max());
+
+ buffer.resize(prevLen + len);
+ memcpy(buffer.data() + prevLen, data, static_cast<size_t>(len));
+ Q_EMIT bytesWritten(len);
+ Q_EMIT m_remoteEndPoint->readyRead();
+ return len;
+}
+
+} // namespace QtCoreHelper
+
+QT_END_NAMESPACE
+
+#include "qiopipe.moc"
#include <QtCore/qdebug.h>
+QT_BEGIN_NAMESPACE
+
namespace QtCoreHelper {
// Data classes for the generic argument data classes. The argument is freed
}
} // namespace QtCoreHelper
+
+QT_END_NAMESPACE
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtCore">
+<typesystem package="PySide6.QtCore"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<?if windows?>
<load-typesystem name="QtCore/typesystem_core_win.xml" generate="yes"/>
<?endif?>
<modify-argument index="return" pyi-type="str"/>
</modify-function>
</function>
- <function signature="qCompress(const uchar*,qsizetype,int)"/>
- <function signature="qCompress(const QByteArray&,int)"/>
- <function signature="qUncompress(const uchar*,qsizetype)"/>
- <function signature="qUncompress(const QByteArray&)"/>
- <function signature="qFormatLogMessage(QtMsgType,const QMessageLogContext&,const QString&)"/>
- <function signature="qSetMessagePattern(const QString&)"/>
+ <!-- Move PyBuffer overload to front to avoid conversion PyBuffer->QByteArray -->
+ <function signature="qCompress(const uchar*,qsizetype,int)" overload-number="0">
+ <modify-function>
+ <modify-argument index="1">
+ <replace-type modified-type="PyBuffer"/>
+ </modify-argument>
+ <inject-code file="../glue/qtcore.cpp" snippet="qcompress-buffer"/>
+ </modify-function>
+ </function>
+ <function signature="qCompress(const QByteArray&,int)" overload-number="1"/>
+ <!-- Move PyBuffer overload to front to avoid conversion PyBuffer->QByteArray -->
+ <function signature="qUncompress(const uchar*,qsizetype)" overload-number="0">
+ <modify-function>
+ <modify-argument index="1">
+ <replace-type modified-type="PyBuffer"/>
+ </modify-argument>
+ <inject-code file="../glue/qtcore.cpp" snippet="quncompress-buffer"/>
+ </modify-function>
+ </function>
+ <function signature="qUncompress(const QByteArray&)" overload-number="1"/>
+ <function signature="qFormatLogMessage(QtMsgType,const QMessageLogContext&,const QString&)"
+ doc-file="qtlogging"/>
+ <function signature="qSetMessagePattern(const QString&)" doc-file="qtlogging"/>
<inject-code class="native" position="beginning" file="../glue/qtcore.cpp" snippet="include-pyside"/>
<inject-code class="native" position="beginning" file="../glue/qtcore.cpp"
snippet="qarg_helper"/>
+ <inject-code class="native" position="beginning" file="../glue/qtcore.cpp"
+ snippet="darwin_permission_plugin"/>
<add-function signature="qDebug(const char*)">
<inject-code file="../glue/qtcore.cpp" snippet="use-stream-for-format-security"/>
<!-- From Qt4.6 ^^^ -->
- <enum-type name="QtMsgType"/>
- <enum-type name="QCborSimpleType"/>
- <enum-type name="QCborKnownTags"/>
- <enum-type name="QCborTag"/>
+ <enum-type name="QtMsgType" doc-file="qtlogging"/>
+ <enum-type name="QCborSimpleType" doc-file="qtcborcommon"/>
+ <enum-type name="QCborKnownTags" doc-file="qtcborcommon"/>
+ <enum-type name="QCborTag" doc-file="qtcborcommon"/>
<primitive-type name="qint8"/>
<primitive-type name="qint16"/>
</conversion-rule>
</primitive-type>
+ <primitive-type name="QLatin1String" target-lang-api-name="PyUnicode">
+ <include file-name="QtCore/qlatin1stringview.h" location="global"/>
+ <conversion-rule>
+ <native-to-target file="../glue/qtcore.cpp" snippet="return-pyunicode-from-qlatin1string"/>
+ <target-to-native>
+ <add-conversion type="PyString" check="qLatin1StringCheck(%in)"
+ file="../glue/qtcore.cpp" snippet="conversion-pystring-qlatin1string"/>
+ </target-to-native>
+ </conversion-rule>
+ </primitive-type>
+
<primitive-type name="QAnyStringView" target-lang-api-name="PyUnicode" view-on="QString">
<include file-name="QAnyStringView" location="global"/>
<conversion-rule>
</primitive-type>
<primitive-type name="QVariant" target-lang-api-name="PyObject">
+ <extra-includes>
+ <include file-name="optional" location="global"/>
+ </extra-includes>
<conversion-rule>
<native-to-target file="../glue/qtcore.cpp" snippet="return-qvariant"/>
<target-to-native>
<enum-type name="WindowType" python-type="IntFlag" flags="WindowFlags"/>
<enum-type name="CursorMoveStyle" since="4.8" revision="4800"/>
+ <inject-code class="target" position="end" file="../glue/qtcore.cpp"
+ snippet="qt-modifier"/>
</namespace-type>
<add-function signature="QEnum(PyObject*)" return-type="PyObject*">
<inject-code class="target" position="end" file="../glue/qtcore.cpp" snippet="qt-pysideinit"/>
<inject-code class="native" position="beginning" file="../glue/qtcore.cpp" snippet="qt-messagehandler"/>
+ <inject-code class="native" position="beginning" file="../glue/qtcore.cpp"
+ snippet="qlatin1string-check"/>
<add-function signature="qInstallMessageHandler(PyObject)" return-type="PyObject">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qt-installmessagehandler"/>
</add-function>
<enum-type name="ClockType" since="4.7"/>
</value-type>
- <object-type name="QAbstractTableModel" polymorphic-id-expression="qobject_cast<QAbstractTableModel*>(%1)">
+ <object-type name="QAbstractTableModel"
+ polymorphic-id-expression="qobject_cast<QAbstractTableModel*>(%B)">
<extra-includes>
<include file-name="QStringList" location="global"/>
<include file-name="QSize" location="global"/>
<enum-type name="System"/>
<enum-type identified-by-value="Unspecified"/>
</value-type>
- <value-type name="QDate" hash-function="PySide::hash" >
- <extra-includes>
- <include file-name="pysideqhash.h" location="global"/>
- </extra-includes>
+ <value-type name="QDate">
<inject-code class="native" position="beginning" file="../glue/qtcore.cpp"
snippet="core-snippets-p-h"/>
<conversion-rule>
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qdate-weeknumber"/>
</modify-function>
</value-type>
- <value-type name="QDateTime" hash-function="PySide::hash">
- <extra-includes>
- <include file-name="pysideqhash.h" location="global"/>
- </extra-includes>
+ <value-type name="QDateTime">
<inject-code class="native" position="beginning" file="../glue/qtcore.cpp"
snippet="core-snippets-p-h"/>
<enum-type name="YearRange"/>
+ <enum-type name="TransitionResolution" since="6.7"/>
<conversion-rule>
<target-to-native>
<add-conversion type="Py_None" file="../glue/qtcore.cpp" snippet="conversion-pynone"/>
<configuration condition="QT_CONFIG(permissions)"/>
</value-type>
- <value-type name="QPoint" hash-function="PySide::hash">
- <extra-includes>
- <include file-name="pysideqhash.h" location="global"/>
- </extra-includes>
+ <value-type name="QPoint">
<add-function signature="__repr__" return-type="PyObject*">
<inject-code class="target" position="beginning">
<insert-template name="repr_code">
</insert-template>
</inject-code>
</add-function>
- <inject-code class="native" position="beginning" file="../glue/qtcore.cpp" snippet="qpoint"/>
<add-function signature="toTuple" return-type="PyObject*">
<inject-code class="target" position="beginning">
<modify-function signature="ry()" remove="all"/>
<!--### -->
</value-type>
- <value-type name="QRect" hash-function="PySide::hash">
- <extra-includes>
- <include file-name="pysideqhash.h" location="global"/>
- </extra-includes>
+ <value-type name="QRect">
<add-function signature="__repr__" return-type="PyObject*">
<inject-code class="target" position="beginning">
<insert-template name="repr_code">
</insert-template>
</inject-code>
</add-function>
- <inject-code class="native" position="beginning" file="../glue/qtcore.cpp" snippet="qrect"/>
<modify-function signature="getCoords(int*,int*,int*,int*)const">
<modify-argument index="return">
</inject-code>
</modify-function>
</value-type>
- <value-type name="QSize" hash-function="PySide::hash">
- <extra-includes>
- <include file-name="pysideqhash.h" location="global"/>
- </extra-includes>
+ <value-type name="QSize">
<add-function signature="__repr__" return-type="PyObject*">
<inject-code class="target" position="beginning">
<insert-template name="repr_code">
</insert-template>
</inject-code>
</add-function>
- <inject-code class="native" position="beginning" file="../glue/qtcore.cpp" snippet="qsize"/>
<add-function signature="toTuple" return-type="PyObject*">
<inject-code class="target" position="beginning">
<!--### -->
</value-type>
- <value-type name="QTime" hash-function="PySide::hash">
- <extra-includes>
- <include file-name="pysideqhash.h" location="global"/>
- </extra-includes>
+ <value-type name="QTime">
<inject-code class="native" position="beginning" file="../glue/qtcore.cpp"
snippet="core-snippets-p-h"/>
<conversion-rule>
qRegisterMetaType<QList<QPersistentModelIndex> >("QList_QPersistentModelIndex");
</inject-code>
<modify-function signature="internalPointer()const">
+ <modify-argument index="return" pyi-type="Any"/>
<inject-code class="target" position="beginning">
<insert-template name="return_internal_pointer" />
</inject-code>
</object-type>
<value-type name="QLocale">
+ <enum-type name="TagSeparator" since="6.7"/>
<enum-type name="Country"/>
<enum-type name="DataSizeFormat" flags="DataSizeFormats"/>
<enum-type name="FloatingPointPrecisionOption" python-type="IntEnum"/>
<rename to="format"/>
</modify-argument>
</modify-function>
- <modify-function signature="toDate(QString,QLocale::FormatType)const">
+ <modify-function signature="toDate(QString,QLocale::FormatType,int)const">
<modify-argument index="2">
<rename to="format"/>
</modify-argument>
</modify-function>
+ <modify-function signature="^toDate(Time)?\(QString,[^,]+,int\)const$">
+ <modify-argument index="3">
+ <replace-default-expression with="1900"/> <!-- private FirstTwoDigitYear -->
+ </modify-argument>
+ </modify-function>
+ <modify-function signature="^toDate(Time)?\(QString,[^,]+,QCalendar,int\)const$">
+ <modify-argument index="4">
+ <replace-default-expression with="1900"/> <!-- private FirstTwoDigitYear -->
+ </modify-argument>
+ </modify-function>
<modify-function signature="toUInt(QString,bool*)const">
<modify-argument index="2">
<remove-argument />
<modify-function signature="removeStaleLockFile()" allow-thread="yes"/>
<modify-function signature="tryLock(int)" allow-thread="yes"/>
<modify-function signature="unlock()" allow-thread="yes"/>
+ <modify-function signature="getLockInfo(qint64*,QString*,QString*)const">
+ <modify-argument index="return" pyi-type="Tuple[int, str, str]">
+ <replace-type modified-type="(int, str, str)"/>
+ </modify-argument>
+ <modify-argument index="1"><remove-argument/></modify-argument>
+ <modify-argument index="2"><remove-argument/></modify-argument>
+ <modify-argument index="3"><remove-argument/></modify-argument>
+ <inject-code class="target" position="beginning"
+ file="../glue/qtcore.cpp" snippet="qlockfile-getlockinfo"/>
+ </modify-function>
</object-type>
<object-type name="QMessageAuthenticationCode"/>
<object-type name="QSignalBlocker">
<add-function signature="operator==(const QItemSelection&)" return-type="bool"/>
<add-function signature="operator!=(const QItemSelection&)" return-type="bool"/>
<!-- For some reason, the empty selection is not seen. Maybe related to the new [default]
- tag in Qt6? -->
- <declare-function signature="QItemSelection()" return-type="QItemSelection" />
+ tag in Qt6?
+ PYSIDE-2756: The return-type attribute is unnecessary -->
+ <declare-function signature="QItemSelection()"/>
<!-- The __add__ function creates a result list, instead of using the inherited type.
Fixed by adding with the correct type. -->
<add-function signature="operator+(QItemSelection)" return-type="QItemSelection">
<value-type name="QItemSelectionRange">
</value-type>
- <object-type name="QAbstractProxyModel" polymorphic-id-expression="qobject_cast<QAbstractProxyModel*>(%1)">
+ <object-type name="QAbstractProxyModel"
+ polymorphic-id-expression="qobject_cast<QAbstractProxyModel*>(%B)">
<extra-includes>
<include file-name="QItemSelection" location="global"/>
<include file-name="QStringList" location="global"/>
<reference-count action="set"/>
</modify-argument>
</modify-function>
-
+ <!-- FIXME PYSIDE 7: Remove this (QT6_DECL_NEW_OVERLOAD_TAIL) -->
+ <modify-function signature="^moveToThread\(.*\)" remove="all"/>
+ <declare-function signature="moveToThread(QThread*)" return-type="bool"/>
<modify-function signature="deleteLater()">
<modify-argument index="this">
<define-ownership owner="c++"/>
</modify-argument>
<modify-argument index="1" pyi-type="Optional[PySide6.QtCore.QObject]"/>
</modify-function>
- <modify-function signature="connect(const QObject*,const char*,const char*,Qt::ConnectionType)const">
+ <!-- Manual overload order fixes PYSIDE-2627
+
+ The addition of the qobject-connect-4-context overload resulted in an
+ automatic overload ordering that prevented the right overload from
+ ever being called if the callable was a QObject. Set a manual order to
+ fix this. -->
+ <modify-function signature="connect(const QObject*,const char*,const char*,Qt::ConnectionType)const"
+ overload-number="0">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-connect-1"/>
</modify-function>
<!-- static version -->
- <modify-function signature="connect(const QObject*,QMetaMethod,const QObject*,QMetaMethod,Qt::ConnectionType)">
+ <modify-function signature="connect(const QObject*,QMetaMethod,const QObject*,QMetaMethod,Qt::ConnectionType)"
+ overload-number="1">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-connect-2"/>
</modify-function>
- <modify-function signature="connect(const QObject*,const char*,const QObject*,const char*,Qt::ConnectionType)">
+ <modify-function signature="connect(const QObject*,const char*,const QObject*,const char*,Qt::ConnectionType)"
+ overload-number="2">
<modify-argument index="5">
<rename to="type"/>
</modify-argument>
</modify-function>
<inject-code class="native" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-connect"/>
<add-function signature="connect(const QObject*@sender@,const char*@signal@,PyCallable*@functor@,Qt::ConnectionType@type@=Qt::AutoConnection)"
- return-type="QMetaObject::Connection" static="yes">
+ return-type="QMetaObject::Connection" static="yes" overload-number="3">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-connect-4"/>
</add-function>
<add-function signature="connect(const QObject*@sender@,const char*@signal@,const QObject*@context@,PyCallable*@functor@,Qt::ConnectionType@type@=Qt::AutoConnection)"
- return-type="QMetaObject::Connection" static="yes">
+ return-type="QMetaObject::Connection" static="yes" overload-number="4">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-connect-4-context"/>
</add-function>
<!-- static version -->
<add-function signature="connect(const char*@signal@,PyCallable*@functor@,Qt::ConnectionType@type@=Qt::AutoConnection)"
- return-type="QMetaObject::Connection">
+ return-type="QMetaObject::Connection" overload-number="5">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-connect-5"/>
</add-function>
<add-function signature="connect(const char*@signal@,const QObject*@receiver@,const char*@method@,Qt::ConnectionType@type@=Qt::AutoConnection)"
- return-type="QMetaObject::Connection">
+ return-type="QMetaObject::Connection" overload-number="6">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-connect-6"/>
</add-function>
</modify-argument>
</modify-function>
</object-type>
- <object-type name="QAbstractListModel" polymorphic-id-expression="qobject_cast<QAbstractListModel*>(%1)">
+ <object-type name="QAbstractListModel"
+ polymorphic-id-expression="qobject_cast<QAbstractListModel*>(%B)">
<extra-includes>
<include file-name="QStringList" location="global"/>
<include file-name="QSize" location="global"/>
</object-type>
<value-type name="QUrlQuery"/>
- <value-type name="QUrl" hash-function="PySide::hash">
+ <value-type name="QUrl">
<!-- Qt5: lots of changes -->
<enum-type name="ComponentFormattingOption" python-type="IntFlag" flags="ComponentFormattingOptions,FormattingOptions"/>
<!-- note: above duplication of attribute is not by default XML compliant! -->
<enum-type name="AceProcessingOption" flags="AceProcessingOptions" since="6.3"/>
<extra-includes>
<include file-name="QStringList" location="global"/>
- <include file-name="pysideqhash.h" location="global"/>
</extra-includes>
<add-function signature="__repr__" return-type="PyObject*">
<inject-code class="target" position="beginning">
<value-type name="QOperatingSystemVersionBase" since="6.3">
<enum-type name="OSType"/>
</value-type>
+ <value-type name="QOperatingSystemVersionUnexported" since="6.3" generate="false"/>
<value-type name="QOperatingSystemVersion">
- <enum-type name="OSType"/>
<modify-function signature="QOperatingSystemVersion(const QOperatingSystemVersionBase&)" remove="all"/>
</value-type>
<object-type name="QLibrary">
<inject-code file="../glue/qtcore.cpp" snippet="unlock"/>
</add-function>
</object-type>
+ <object-type name="QIOPipe"/>
<value-type name="QGenericArgumentHolder"/>
<value-type name="QGenericReturnArgumentHolder"/>
</namespace-type>
</modify-argument>
<inject-code file="../glue/qtcore.cpp" snippet="qtranslator-load"/>
</modify-function>
+ <modify-function signature="translate(const char*,const char*, const char*,int)const">
+ <modify-argument index="1" pyi-type="str"/>
+ <modify-argument index="2" pyi-type="str"/>
+ <modify-argument index="3" pyi-type="Optional[str]"/>
+ </modify-function>
</object-type>
<object-type name="QWaitCondition">
<configuration condition="QT_CONFIG(thread)"/>
<extra-includes>
<include file-name="pysidestaticstrings.h" location="global"/>
</extra-includes>
+ <inject-code class="native" position="beginning" file="../glue/qtcore.cpp"
+ snippet="qtimer-singleshot-functorclass"/>
<modify-function signature="singleShot(int,const QObject*,const char*)">
- <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qtimer-singleshot-1"/>
+ <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qtimer-singleshot-direct-mapping"/>
</modify-function>
- <add-function signature="singleShot(int,PyCallable*)" static="yes">
- <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qtimer-singleshot-2"/>
+ <add-function signature="singleShot(int@msec@,PyCallable*@functor@)" static="yes">
+ <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qtimer-singleshot-functor"/>
</add-function>
<add-function signature="singleShot(int@msec@,const QObject*@context@,PyCallable*@functor@)" static="yes">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qtimer-singleshot-functor-context"/>
<include file-name="pysidecleanup.h" location="global"/>
<include file-name="pysideqapp.h" location="global"/>
</extra-includes>
- <!-- constructor documentation -->
- <inject-documentation format="target" mode="append">
-.. class:: QCoreApplication(args)
-
- Constructs a Qt kernel application. Kernel applications are applications
- without a graphical user interface. These type of applications are used
- at the console or as server processes.
-
- The *args* argument is processed by the application, and made available
- in a more convenient form by the :meth:`~QCoreApplication.arguments()`
- method.
- </inject-documentation>
- <add-function signature="QCoreApplication(QStringList)">
+ <add-function signature="QCoreApplication(QStringList@args@)">
<inject-code file="../glue/qtcore.cpp" snippet="qcoreapplication-1"/>
+ <inject-documentation format="target" mode="append">
+ Constructs a Qt kernel application. Kernel applications are applications
+ without a graphical user interface. These type of applications are used
+ at the console or as server processes.
+
+ The *args* argument is processed by the application, and made available
+ in a more convenient form by the :meth:`~PySide6.QtCore.QCoreApplication.arguments()`
+ method.
+ </inject-documentation>
</add-function>
<add-function signature="QCoreApplication()">
<inject-code file="../glue/qtcore.cpp" snippet="qcoreapplication-2"/>
file="../glue/qtcore.cpp" snippet="repr-qevent"/>
</add-function>
</object-type>
- <object-type name="QChildEvent" polymorphic-id-expression="%1->type() == QEvent::ChildAdded || %1->type() == QEvent::ChildPolished || %1->type() == QEvent::ChildRemoved">
+ <object-type name="QChildEvent"
+ polymorphic-id-expression="%B->type() == QEvent::ChildAdded || %B->type() == QEvent::ChildPolished || %B->type() == QEvent::ChildRemoved">
<modify-function signature="child()const">
<modify-argument index="return">
<define-ownership class="target" owner="default"/>
</modify-argument>
</modify-function>
</object-type>
- <object-type name="QTimerEvent" polymorphic-id-expression="%1->type() == QEvent::Timer"/>
- <object-type name="QDynamicPropertyChangeEvent" polymorphic-id-expression="%1->type() == QEvent::DynamicPropertyChange"/>
+ <object-type name="QTimerEvent"
+ polymorphic-id-expression="%B->type() == QEvent::Timer"/>
+ <object-type name="QDynamicPropertyChangeEvent"
+ polymorphic-id-expression="%B->type() == QEvent::DynamicPropertyChange"/>
<object-type name="QDataStream" stream="yes">
<add-function signature="readQVariant()" return-type="QVariant">
<inject-code class="target" position="end" file="../glue/qtcore.cpp" snippet="stream-read-method"/>
</add-function>
- <modify-function signature="readRawData(char*,int)">
+ <modify-function signature="readRawData(char*,qint64)">
<modify-argument index="return" pyi-type="bytes"/>
<modify-argument index="1">
<remove-argument />
<inject-code class="target" position="beginning"
file="../glue/qtcore.cpp" snippet="qdatastream-writerawdata-pybuffer"/>
</add-function>
- <modify-function signature="writeRawData(const char*,int)">
+ <modify-function signature="writeRawData(const char*,qint64)">
<modify-argument index="1" pyi-type="str"/>
<modify-argument index="2">
<remove-argument />
<add-function signature="writeString(QString)">
<inject-code class="target" position="end" file="../glue/qtcore.cpp" snippet="stream-write-method"/>
</add-function>
- <modify-function signature="readBytes(char*&,uint&)">
+ <modify-function signature="readBytes(char*&,qint64&)">
<modify-argument index="return">
<replace-type modified-type="PyTuple"/>
</modify-argument>
</modify-argument>
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qdatastream-read-bytes"/>
</modify-function>
+ <modify-function signature="readBytes(char*&,uint&)" remove="all"/>
- <modify-function signature="writeBytes(const char*,uint)">
+ <modify-function signature="writeBytes(const char*,qint64)">
<modify-argument index="1">
<replace-type modified-type="PyBuffer"/>
<conversion-rule class="native">
</object-type>
<value-type name="QModelIndex">
<modify-function signature="internalPointer()const">
+ <modify-argument index="return" pyi-type="Any"/>
<inject-code class="target" position="beginning">
<insert-template name="return_internal_pointer" />
</inject-code>
<suppress-warning text="^signature 'readStringChunk\(char.*in 'QCborStreamReader' not found.*$"/>
<!-- TODO: this need be removed -->
- <suppress-warning text="^skipping function '.*', unmatched return type '.*$"/>
- <suppress-warning text="^skipping function '.*', unmatched type '.*$"/>
- <suppress-warning text="skipping field 'QStringConverter::iface' with unmatched type 'QStringConverter::Interface'"/>
- <suppress-warning text="skipping field 'Qt::Uninitialized' with unmatched type 'Qt::Initialization'"/>
- <suppress-warning text="skipping field 'State::clearFn' with unmatched type 'void'"/>
+ <suppress-warning text="^skipping.*function '.*', unmatched return type '.*$"/>
+ <suppress-warning text="^skipping.*function '.*', unmatched type '.*$"/>
+ <suppress-warning text="skipping protected field 'QStringConverter::iface' with unmatched type 'QStringConverter::Interface'"/>
+ <suppress-warning text="^skipping public field 'Qt::.*' with unmatched type 'Qt::.*ordering'$"/>
+ <suppress-warning text="skipping public field 'Qt::Uninitialized' with unmatched type 'Qt::Initialization'"/>
+ <suppress-warning text="skipping public field 'State::clearFn' with unmatched type 'void'"/>
<suppress-warning text="template baseclass 'QListSpecialMethods<T>' of 'QList' is not known"/>
<suppress-warning text="^.*inherits from a non polymorphic type.*QIODeviceBase.*type discovery based on RTTI is impossible.*$"/>
<suppress-warning text="Base class 'QOperatingSystemVersionUnexported' of class 'QOperatingSystemVersion' not found in the type system for setting up inheritance."/>
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtDBus">
+<typesystem package="PySide6.QtDBus"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<namespace-type name="QDBus">
<value-type name="QDBusError">
<enum-type name="ErrorType"/>
</value-type>
- <object-type name="QDBusInterface"/>
+ <object-type name="QDBusInterface" qt-metaobject="no">
+ <inject-documentation format="target" mode="append">
+ DBus signals can be captured with string-based connections
+ (see :ref:`signals-and-slots-strings`).
+ </inject-documentation>
+ </object-type>
<value-type name="QDBusMessage">
<enum-type name="MessageType"/>
</value-type>
${QtCore_GEN_DIR}
${QtGui_GEN_DIR})
-set(QtDataVisualization_libraries pyside6
- ${Qt${QT_MAJOR_VERSION}DataVisualization_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Gui_LIBRARIES})
+set(QtDataVisualization_libraries pyside6
+ ${Qt${QT_MAJOR_VERSION}DataVisualization_LIBRARIES})
set(QtDataVisualization_deps QtCore QtGui)
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtDataVisualization">
+<typesystem package="PySide6.QtDataVisualization"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<extra-includes>
<include file-name="qtdatavisualization_helper.h" location="global"/>
${QtDesigner_BINARY_DIR}
${QtCore_GEN_DIR}
${QtGui_GEN_DIR}
- ${QtWidgets_GEN_DIR}
- )
+ ${QtWidgets_GEN_DIR})
+
set(QtDesigner_libraries pyside6
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Gui_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Widgets_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Designer_LIBRARIES}
- )
+ ${Qt${QT_MAJOR_VERSION}Designer_LIBRARIES})
+
set(QtDesigner_deps QtWidgets)
create_pyside_module(NAME QtDesigner
#include <shiboken.h>
#include <bindingmanager.h>
+QT_BEGIN_NAMESPACE
+
static QString pyStringToQString(PyObject *s)
{
const char *utf8 = _PepUnicode_AsString(s);
{
instance()->m_customWidgets.append(c);
}
+
+QT_END_NAMESPACE
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtDesigner">
+<typesystem package="PySide6.QtDesigner"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtWidgets/typesystem_widgets.xml" generate="no"/>
<rejection class="qdesigner_internal"/>
project(QtGraphs)
+set(QtGraphs_DROPPED_ENTRIES)
+
list(APPEND QtGraphs_src "${QtGraphs_SOURCE_DIR}/qtgraphs_helper.cpp")
set(QtGraphs_SRC
+# 3D
${QtGraphs_GEN_DIR}/qabstract3daxis_wrapper.cpp
${QtGraphs_GEN_DIR}/qcategory3daxis_wrapper.cpp
${QtGraphs_GEN_DIR}/qlogvalue3daxisformatter_wrapper.cpp
${QtGraphs_GEN_DIR}/qsurfacedataitem_wrapper.cpp
${QtGraphs_GEN_DIR}/qsurfacedataproxy_wrapper.cpp
${QtGraphs_GEN_DIR}/q3dbars_wrapper.cpp
-${QtGraphs_GEN_DIR}/q3dcamera_wrapper.cpp
-${QtGraphs_GEN_DIR}/q3dlight_wrapper.cpp
-${QtGraphs_GEN_DIR}/q3dobject_wrapper.cpp
${QtGraphs_GEN_DIR}/q3dscatter_wrapper.cpp
${QtGraphs_GEN_DIR}/q3dscene_wrapper.cpp
${QtGraphs_GEN_DIR}/q3dsurface_wrapper.cpp
${QtGraphs_GEN_DIR}/qabstract3dinputhandler_wrapper.cpp
${QtGraphs_GEN_DIR}/qtouch3dinputhandler_wrapper.cpp
${QtGraphs_GEN_DIR}/q3dtheme_wrapper.cpp
+# 2D
+${QtGraphs_GEN_DIR}/qbarcategoryaxis_wrapper.cpp
+${QtGraphs_GEN_DIR}/qabstractaxis_wrapper.cpp
+${QtGraphs_GEN_DIR}/qvalueaxis_wrapper.cpp
+${QtGraphs_GEN_DIR}/qabstractbarseries_wrapper.cpp
+${QtGraphs_GEN_DIR}/qbarseries_wrapper.cpp
+${QtGraphs_GEN_DIR}/qbarset_wrapper.cpp
+${QtGraphs_GEN_DIR}/qlineseries_wrapper.cpp
+${QtGraphs_GEN_DIR}/qabstractseries_wrapper.cpp
+${QtGraphs_GEN_DIR}/qscatterseries_wrapper.cpp
+${QtGraphs_GEN_DIR}/qgraphtheme_wrapper.cpp
+${QtGraphs_GEN_DIR}/qseriestheme_wrapper.cpp
+${QtGraphs_GEN_DIR}/qxyseries_wrapper.cpp
# module is always needed
${QtGraphs_GEN_DIR}/qtgraphs_module_wrapper.cpp
)
${Qt${QT_MAJOR_VERSION}Gui_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Widgets_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Qml_INCLUDE_DIRS}
- ${Qt${QT_MAJOR_VERSION}OpenGL_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Quick_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}QuickWidgets_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Graphs_INCLUDE_DIRS}
${QtGui_GEN_DIR}
${QtWidgets_GEN_DIR}
${QtQml_GEN_DIR}
- ${QtOpenGL_GEN_DIR}
${QtQuick_GEN_DIR}
${QtQuickWidgets_GEN_DIR}
${QtQuick3D_GEN_DIR})
set(QtGraphs_libraries pyside6
- ${Qt${QT_MAJOR_VERSION}Graphs_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Network_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Gui_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Widgets_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Qml_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}OpenGL_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Quick_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}QuickWidgets_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Quick3D_LIBRARIES})
+ ${Qt${QT_MAJOR_VERSION}Graphs_LIBRARIES})
+
+set(QtGraphs_deps QtCore QtNetwork QtGui QtWidgets QtQml QtQuick QtQuickWidgets QtQuick3D)
-set(QtGraphs_deps QtCore QtNetwork QtGui QtWidgets QtQml QtOpenGL QtQuick QtQuickWidgets QtQuick3D)
+check_qt_opengl("Graphs" QtGraphs_include_dirs QtGraphs_deps
+ QtGraphs_DROPPED_ENTRIES)
create_pyside_module(NAME QtGraphs
INCLUDE_DIRS QtGraphs_include_dirs
DEPS QtGraphs_deps
TYPESYSTEM_PATH QtGraphs_SOURCE_DIR
SOURCES QtGraphs_SRC
- STATIC_SOURCES QtGraphs_src)
+ STATIC_SOURCES QtGraphs_src
+ DROPPED_ENTRIES QtGraphs_DROPPED_ENTRIES)
install(FILES ${pyside6_SOURCE_DIR}/qtgraphs_helper.h
DESTINATION include/PySide6/QtGraphs)
const qsizetype zStride = zStrideBytes / sizeof(T);
double z = zStart;
for (qsizetype zi = 0; zi < zSize; ++zi) {
- auto *row = new QSurfaceDataRow;
- row->reserve(xSize);
- result->append(row);
+ QSurfaceDataRow row;
+ row.reserve(xSize);
double x = xStart;
auto *rowDataEnd = data + xSize;
for (auto *d = data; d < rowDataEnd; ++d) {
- row->append(QSurfaceDataItem(QVector3D(x, *d, z)));
+ row.append(QSurfaceDataItem(QVector3D(x, *d, z)));
x += deltaX;
}
+ result->append(row);
data += zStride;
z += deltaZ;
}
}
-QSurfaceDataArray *surfaceDataFromNp(double xStart, double deltaX, double zStart, double deltaZ,
- PyObject *pyData)
+QSurfaceDataArray surfaceDataFromNp(double xStart, double deltaX, double zStart, double deltaZ,
+ PyObject *pyData)
{
static const char funcName[] = "QSurfaceDataProxy.resetArrayNp";
- auto *result = new QSurfaceDataArray;
+ QSurfaceDataArray result;
auto view = Shiboken::Numpy::View::fromPyObject(pyData);
if (!view) {
switch (view.type) {
case Shiboken::Numpy::View::Int16:
populateArray(xStart, deltaX, zStart, deltaZ, xSize, zSize, view.stride[0],
- reinterpret_cast<const int16_t *>(view.data), result);
+ reinterpret_cast<const int16_t *>(view.data), &result);
break;
case Shiboken::Numpy::View::Unsigned16:
populateArray(xStart, deltaX, zStart, deltaZ, xSize, zSize, view.stride[0],
- reinterpret_cast<const uint16_t *>(view.data), result);
+ reinterpret_cast<const uint16_t *>(view.data), &result);
break;
case Shiboken::Numpy::View::Int:
populateArray(xStart, deltaX, zStart, deltaZ, xSize, zSize, view.stride[0],
- reinterpret_cast<const int *>(view.data), result);
+ reinterpret_cast<const int *>(view.data), &result);
break;
case Shiboken::Numpy::View::Unsigned:
populateArray(xStart, deltaX, zStart, deltaZ, xSize, zSize, view.stride[0],
- reinterpret_cast<const unsigned *>(view.data), result);
+ reinterpret_cast<const unsigned *>(view.data), &result);
break;
case Shiboken::Numpy::View::Int64:
populateArray(xStart, deltaX, zStart, deltaZ, xSize, zSize, view.stride[0],
- reinterpret_cast<const int64_t *>(view.data), result);
+ reinterpret_cast<const int64_t *>(view.data), &result);
break;
case Shiboken::Numpy::View::Unsigned64:
populateArray(xStart, deltaX, zStart, deltaZ, xSize, zSize, view.stride[0],
- reinterpret_cast<const uint64_t *>(view.data), result);
+ reinterpret_cast<const uint64_t *>(view.data), &result);
break;
case Shiboken::Numpy::View::Float:
populateArray(xStart, deltaX, zStart, deltaZ, xSize, zSize, view.stride[0],
- reinterpret_cast<const float *>(view.data), result);
+ reinterpret_cast<const float *>(view.data), &result);
break;
case Shiboken::Numpy::View::Double:
populateArray(xStart, deltaX, zStart, deltaZ, xSize, zSize, view.stride[0],
- reinterpret_cast<const double *>(view.data), result);
+ reinterpret_cast<const double *>(view.data), &result);
break;
}
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtGraphs">
+<typesystem package="PySide6.QtGraphs"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<extra-includes>
<include file-name="qtgraphs_helper.h" location="global"/>
<function signature="qDefaultSurfaceFormat(bool)"/>
- <primitive-type name="QBarDataArray">
- <include file-name="qbardataproxy.h" location="global"/>
- <conversion-rule>
- <native-to-target>
- <insert-template name="cppqlistofptrtoqlists_to_py_conversion">
- <replace from="%INTYPE_0" to="QBarDataItem"/>
- </insert-template>
- </native-to-target>
- <target-to-native>
- <add-conversion type="PySequence">
- <insert-template name="py_to_cppqlistofptrtoqlists_conversion">
- <replace from="%OUTTYPE_0" to="QBarDataItem"/>
- </insert-template>
- </add-conversion>
- </target-to-native>
- </conversion-rule>
- </primitive-type>
- <primitive-type name="QSurfaceDataArray">
- <include file-name="qsurfacedataproxy.h" location="global"/>
- <conversion-rule>
- <native-to-target>
- <insert-template name="cppqlistofptrtoqlists_to_py_conversion">
- <replace from="%INTYPE_0" to="QSurfaceDataItem"/>
- </insert-template>
- </native-to-target>
- <target-to-native>
- <add-conversion type="PySequence">
- <insert-template name="py_to_cppqlistofptrtoqlists_conversion">
- <replace from="%OUTTYPE_0" to="QSurfaceDataItem"/>
- </insert-template>
- </add-conversion>
- </target-to-native>
- </conversion-rule>
- </primitive-type>
<object-type name="QAbstract3DAxis">
<enum-type name="AxisOrientation"/>
</modify-function>
</object-type>
<object-type name="QValue3DAxisFormatter">
- <inject-code class="native" position="beginning" file="../glue/qtdatavisualization.cpp"
- snippet="qvalue3daxisformatter-friend"/>
<modify-function signature="createNewInstance() const">
<modify-argument index="return">
<define-ownership class="native" owner="c++"/>
<define-ownership class="target" owner="default"/>
</modify-argument>
</modify-function>
- <!-- PYSIDE-2025: gridPositions(), labelPositions(), labelStrings() return
- non-const-references to lists for modifications. Add setters for them. -->
- <add-function signature="setGridPositions(const QList<float>&@grid_positions@)">
- <inject-documentation format="target" mode="append">
- Sets the normalized grid line positions to ``grid_positions``.
- </inject-documentation>
- <inject-code class="target" position="beginning" file="../glue/qtdatavisualization.cpp"
- snippet="qvalue3daxisformatter-setgridpositions"/>
- </add-function>
- <add-function signature="setLabelPositions(const QList<float>&@label_positions@)">
- <inject-documentation format="target" mode="append">
- Sets the normalized label positions to ``label_positions``.
- </inject-documentation>
- <inject-code class="target" position="beginning" file="../glue/qtdatavisualization.cpp"
- snippet="qvalue3daxisformatter-setlabelpositions"/>
- </add-function>
- <add-function signature="setLabelStrings(const QStringList&@label_strings@)">
- <inject-documentation format="target" mode="append">
- Sets the label strings to ``label_strings``.
- </inject-documentation>
- <inject-code class="target" position="beginning" file="../glue/qtdatavisualization.cpp"
- snippet="qvalue3daxisformatter-setlabelstrings"/>
- </add-function>
</object-type>
<object-type name="QAbstract3DSeries">
<enum-type name="Mesh"/>
</object-type>
<value-type name="QBarDataItem"/>
<object-type name="QBarDataProxy">
- <modify-function signature="resetArray(QBarDataArray*)" remove="all"/>
- <add-function signature="resetArray(const QBarDataArray&)">
- <inject-code class="target" position="beginning" file="../glue/qtdatavisualization.cpp"
- snippet="dataproxy-resetarray"/>
- </add-function>
- <modify-function signature="resetArray(QBarDataArray*,const QStringList&,const QStringList&)"
- remove="all"/>
- <add-function signature="resetArray(const QBarDataArray&,const QStringList&,const QStringList&)">
- <inject-code class="target" position="beginning" file="../glue/qtdatavisualization.cpp"
- snippet="dataproxy-resetarray2"/>
- </add-function>
- <modify-function signature="resetArray(QBarDataArray*,const QStringList&,const QStringList&)">
- <modify-argument index="1">
- <parent index="this" action="add"/>
- </modify-argument>
- </modify-function>
-
- <!-- PYSIDE-1438: Replace all add/set/insertRow() taking a 'QList*' by overloads
- taking 'const QList &' since an allocated list needs to be passed. -->
- <modify-function signature="addRow(QList<QBarDataItem>*)" remove="all"/>
- <add-function signature="addRow(const QList<QBarDataItem>&)" return-type="int">
- <inject-code class="target" position="beginning" file="../glue/qtdatavisualization.cpp"
- snippet="dataproxy-addrow"/>
- </add-function>
- <modify-function signature="addRow(QList<QBarDataItem>*,const QString&)" remove="all"/>
- <add-function signature="addRow(const QList<QBarDataItem>&,const QString&)"
- return-type="int">
- <inject-code class="target" position="beginning" file="../glue/qtdatavisualization.cpp"
- snippet="dataproxy-addrow-string"/>
- </add-function>
-
- <modify-function signature="insertRow(int,QList<QBarDataItem>*)" remove="all"/>
- <add-function signature="insertRow(int,const QList<QBarDataItem>&)">
- <inject-code class="target" position="beginning" file="../glue/qtdatavisualization.cpp"
- snippet="dataproxy-insertrow"/>
- </add-function>
- <modify-function signature="insertRow(int,QList<QBarDataItem>*,const QString&)" remove="all"/>
- <add-function signature="insertRow(int,const QList<QBarDataItem>&, const QString&)">
- <inject-code class="target" position="beginning" file="../glue/qtdatavisualization.cpp"
- snippet="dataproxy-insertrow-string"/>
- </add-function>
-
- <modify-function signature="setRow(int,QList<QBarDataItem>*)" remove="all"/>
- <add-function signature="setRow(int,const QList<QBarDataItem>&)">
- <inject-code class="target" position="beginning" file="../glue/qtdatavisualization.cpp"
- snippet="dataproxy-setrow"/>
- </add-function>
- <modify-function signature="setRow(int,QList<QBarDataItem>*,const QString&)" remove="all"/>
- <add-function signature="setRow(int,const QList<QBarDataItem>&,const QString&)">
- <inject-code class="target" position="beginning" file="../glue/qtdatavisualization.cpp"
- snippet="dataproxy-setrow-string"/>
- </add-function>
-
- <modify-function signature="addRows(const QBarDataArray&)">
- <modify-argument index="1">
- <parent index="this" action="add"/>
- </modify-argument>
- </modify-function>
- <modify-function signature="addRows(const QBarDataArray&, const QStringList&)">
- <modify-argument index="1">
- <parent index="this" action="add"/>
- </modify-argument>
- </modify-function>
- <modify-function signature="insertRows(int, const QBarDataArray&)">
- <modify-argument index="2">
- <parent index="this" action="add"/>
- </modify-argument>
- </modify-function>
- <modify-function signature="insertRows(int, const QBarDataArray&, const QStringList&)">
- <modify-argument index="2">
- <parent index="this" action="add"/>
- </modify-argument>
- </modify-function>
- <modify-function signature="setRows(int, const QBarDataArray&)">
- <modify-argument index="2">
- <parent index="this" action="add"/>
- </modify-argument>
- </modify-function>
- <modify-function signature="setRows(int, const QBarDataArray&, const QStringList&)">
- <modify-argument index="2">
- <parent index="this" action="add"/>
- </modify-argument>
- </modify-function>
</object-type>
<object-type name="QCustom3DItem"/>
<object-type name="QCustom3DLabel"/>
</object-type>
<value-type name="QScatterDataItem"/>
<object-type name="QScatterDataProxy">
- <modify-function signature="resetArray(QList<QScatterDataItem>*)"
- remove="all"/>
- <add-function signature="resetArray(QList<QScatterDataItem>*)">
- <inject-code class="target" position="beginning" file="../glue/qtdatavisualization.cpp"
- snippet="scatterdataproxy-resetarray"/>
- </add-function>
- <modify-function signature="addItem(const QScatterDataItem&)">
- <modify-argument index="1">
- <parent index="this" action="add"/>
- </modify-argument>
- </modify-function>
- <modify-function signature="addItems(const QList<QScatterDataItem>&)">
- <modify-argument index="1">
- <parent index="this" action="add"/>
- </modify-argument>
- </modify-function>
- <modify-function signature="insertItem(int, const QScatterDataItem&)">
- <modify-argument index="2">
- <parent index="this" action="add"/>
- </modify-argument>
- </modify-function>
- <modify-function signature="insertItems(int, const QList<QScatterDataItem>&)">
- <modify-argument index="2">
- <parent index="this" action="add"/>
- </modify-argument>
- </modify-function>
- <modify-function signature="setItem(int, const QScatterDataItem&)">
- <modify-argument index="2">
- <parent index="this" action="add"/>
- </modify-argument>
- </modify-function>
- <modify-function signature="setItems(int, const QList<QScatterDataItem>&)">
- <modify-argument index="2">
- <parent index="this" action="add"/>
- </modify-argument>
- </modify-function>
</object-type>
<object-type name="QSurface3DSeries">
<enum-type name="DrawFlag" flags="DrawFlags"/>
#include <sbknumpycheck.h>
#include <qtgraphs_helper.h>
</inject-code>
- <!-- PYSIDE-1438: Replace all add/set/insertRow() taking a 'QList*' by overloads
- taking 'const QList &' since an allocated list needs to be passed. -->
- <modify-function signature="addRow(QList<QSurfaceDataItem>*)" remove="all"/>
- <add-function signature="addRow(const QList<QSurfaceDataItem>&)" return-type="int">
- <inject-code class="target" position="beginning" file="../glue/qtdatavisualization.cpp"
- snippet="dataproxy-addrow"/>
- </add-function>
-
- <modify-function signature="insertRow(int,QList<QSurfaceDataItem>*)" remove="all"/>
- <add-function signature="insertRow(int,const QList<QSurfaceDataItem>&)">
- <inject-code class="target" position="beginning" file="../glue/qtdatavisualization.cpp"
- snippet="dataproxy-insertrow"/>
- </add-function>
-
- <modify-function signature="setRow(int,QList<QSurfaceDataItem>*)" remove="all"/>
- <add-function signature="setRow(int,const QList<QSurfaceDataItem>&)">
- <inject-code class="target" position="beginning" file="../glue/qtdatavisualization.cpp"
- snippet="dataproxy-setrow"/>
- </add-function>
-
- <modify-function signature="resetArray(QSurfaceDataArray*)" remove="all"/>
- <add-function signature="resetArray(const QSurfaceDataArray&)">
- <inject-code class="target" position="beginning" file="../glue/qtdatavisualization.cpp"
- snippet="dataproxy-resetarray"/>
- <modify-argument index="1">
- <parent index="this" action="add"/>
- </modify-argument>
- </add-function>
-
<add-function signature="resetArrayNp(double@x@,double@deltaX@,double@z@,double@deltaZ@,PyArrayObject*@data@)">
<inject-code file="../glue/qtgraphs.cpp"
snippet="graphs-qsurfacedataproxy-resetarraynp"/>
</modify-argument>
</modify-function>
</object-type>
- <object-type name="Q3DCamera">
- <enum-type name="CameraPreset"/>
- </object-type>
- <object-type name="Q3DLight"/>
- <object-type name="Q3DObject"/>
<object-type name="Q3DScatter">
<modify-function signature="addAxis(QValue3DAxis*)">
<modify-argument index="1">
<enum-type name="RenderingMode"/>
<enum-type name="SelectionFlag" flags="SelectionFlags"/>
<enum-type name="ShadowQuality"/>
+ <enum-type name="CameraPreset" since="6.7"/>
<modify-function signature="addCustomItem(QCustom3DItem*)">
<modify-argument index="1">
<parent index="this" action="add"/>
<enum-type name="ColorStyle"/>
<enum-type name="Theme"/>
</object-type>
+
+ <!-- 2D -->
+ <object-type name="QBarCategoryAxis"/>
+ <object-type name="QAbstractAxis">
+ <enum-type name="AxisType"/>
+ </object-type>
+ <object-type name="QValueAxis"/>
+ <object-type name="QAbstractBarSeries">
+ <enum-type name="LabelsPosition"/>
+ <modify-function signature="append(QBarSet*)">
+ <modify-argument index="1">
+ <parent index="this" action="add"/>
+ </modify-argument>
+ </modify-function>
+ <modify-function signature="append(QList<QBarSet*>)">
+ <modify-argument index="1">
+ <parent index="this" action="add"/>
+ </modify-argument>
+ </modify-function>
+ <modify-function signature="insert(int,QBarSet*)">
+ <modify-argument index="2">
+ <parent index="this" action="add"/>
+ </modify-argument>
+ </modify-function>
+ <modify-function signature="take(QBarSet*)">
+ <modify-argument index="1">
+ <parent index="this" action="add"/>
+ </modify-argument>
+ </modify-function>
+ </object-type>
+ <object-type name="QBarSeries"/>
+ <object-type name="QBarSet"/>
+ <object-type name="QLineSeries"/>
+ <object-type name="QAbstractSeries">
+ <enum-type name="SeriesType"/>
+ </object-type>
+ <object-type name="QScatterSeries"/>
+ <object-type name="QGraphTheme">
+ <enum-type name="ColorTheme"/>
+ </object-type>
+ <object-type name="QSeriesTheme">
+ <enum-type name="SeriesColorTheme"/>
+ </object-type>
+ <object-type name="QXYSeries">
+ </object-type>
+
<extra-includes>
<include file-name="qutils.h" location="global"/>
</extra-includes>
${QtGui_GEN_DIR}/qrhicomputepipeline_wrapper.cpp
${QtGui_GEN_DIR}/qrhidepthstencilclearvalue_wrapper.cpp
${QtGui_GEN_DIR}/qrhidriverinfo_wrapper.cpp
-${QtGui_GEN_DIR}/qrhigles2initparams_wrapper.cpp
-${QtGui_GEN_DIR}/qrhigles2nativehandles_wrapper.cpp
${QtGui_GEN_DIR}/qrhigraphicspipeline_targetblend_wrapper.cpp
${QtGui_GEN_DIR}/qrhigraphicspipeline_wrapper.cpp
+${QtGui_GEN_DIR}/qrhigraphicspipeline_stencilopstate_wrapper.cpp
${QtGui_GEN_DIR}/qrhiinitparams_wrapper.cpp
${QtGui_GEN_DIR}/qrhinativehandles_wrapper.cpp
${QtGui_GEN_DIR}/qrhinullinitparams_wrapper.cpp
${QtGui_GEN_DIR}/qaccessibleeditabletextinterface_wrapper.cpp
${QtGui_GEN_DIR}/qaccessibleevent_wrapper.cpp
${QtGui_GEN_DIR}/qaccessibleobject_wrapper.cpp
+${QtGui_GEN_DIR}/qaccessibleselectioninterface_wrapper.cpp
${QtGui_GEN_DIR}/qaccessiblestatechangeevent_wrapper.cpp
${QtGui_GEN_DIR}/qaccessibletablecellinterface_wrapper.cpp
${QtGui_GEN_DIR}/qaccessibletablemodelchangeevent_wrapper.cpp
${QtGui_GEN_DIR}/qbackingstore_wrapper.cpp
${QtGui_GEN_DIR}/qbitmap_wrapper.cpp
${QtGui_GEN_DIR}/qbrush_wrapper.cpp
+${QtGui_GEN_DIR}/qchildwindowevent_wrapper.cpp
${QtGui_GEN_DIR}/qclipboard_wrapper.cpp
${QtGui_GEN_DIR}/qcloseevent_wrapper.cpp
${QtGui_GEN_DIR}/qcolor_wrapper.cpp
${QtGui_GEN_DIR}/qfileopenevent_wrapper.cpp
${QtGui_GEN_DIR}/qfocusevent_wrapper.cpp
${QtGui_GEN_DIR}/qfont_wrapper.cpp
+${QtGui_GEN_DIR}/qfont_tag_wrapper.cpp
${QtGui_GEN_DIR}/qfontdatabase_wrapper.cpp
${QtGui_GEN_DIR}/qfontinfo_wrapper.cpp
${QtGui_GEN_DIR}/qfontmetrics_wrapper.cpp
${QtGui_GEN_DIR}/qmoveevent_wrapper.cpp
${QtGui_GEN_DIR}/qmovie_wrapper.cpp
${QtGui_GEN_DIR}/qnativegestureevent_wrapper.cpp
+${QtGui_GEN_DIR}/qnativeinterface_wrapper.cpp
${QtGui_GEN_DIR}/qoffscreensurface_wrapper.cpp
-${QtGui_GEN_DIR}/qopenglcontextgroup_wrapper.cpp
-${QtGui_GEN_DIR}/qopenglextrafunctions_wrapper.cpp
-${QtGui_GEN_DIR}/qopenglfunctions_wrapper.cpp
${QtGui_GEN_DIR}/qpagedpaintdevice_wrapper.cpp
${QtGui_GEN_DIR}/qpagelayout_wrapper.cpp
${QtGui_GEN_DIR}/qpageranges_wrapper.cpp
${QtGui_GEN_DIR}/qstandarditem_wrapper.cpp
${QtGui_GEN_DIR}/qstandarditemmodel_wrapper.cpp
${QtGui_GEN_DIR}/qstatustipevent_wrapper.cpp
-${QtGui_GEN_DIR}/qopenglcontext_wrapper.cpp
${QtGui_GEN_DIR}/qaccessible_state_wrapper.cpp
${QtGui_GEN_DIR}/qaccessibleinterface_wrapper.cpp
${QtGui_GEN_DIR}/qscreen_wrapper.cpp
${QtGui_GEN_DIR}/qtgui_module_wrapper.cpp
)
+get_property(QtGui_enabled_features TARGET Qt${QT_MAJOR_VERSION}::Gui
+ PROPERTY QT_ENABLED_PUBLIC_FEATURES)
+
+if("xcb" IN_LIST QtGui_enabled_features)
+ list(APPEND QtGui_SRC
+ ${QtGui_GEN_DIR}/qnativeinterface_qx11application_wrapper.cpp)
+elseif(WIN32)
+ list(APPEND QtGui_SRC
+ ${QtGui_GEN_DIR}/qnativeinterface_qwindowsscreen_wrapper.cpp)
+endif()
+
+if("opengl" IN_LIST QtGui_enabled_features OR "opengles2" IN_LIST QtGui_enabled_features
+ OR "opengles3" IN_LIST QtGui_enabled_features)
+ list(APPEND QtGui_SRC
+ ${QtGui_GEN_DIR}/qopenglcontextgroup_wrapper.cpp
+ ${QtGui_GEN_DIR}/qopenglextrafunctions_wrapper.cpp
+ ${QtGui_GEN_DIR}/qopenglfunctions_wrapper.cpp
+ ${QtGui_GEN_DIR}/qopenglcontext_wrapper.cpp
+ ${QtGui_GEN_DIR}/qrhigles2initparams_wrapper.cpp
+ ${QtGui_GEN_DIR}/qrhigles2nativehandles_wrapper.cpp)
+else()
+ list(APPEND QtGui_DROPPED_ENTRIES
+ QOpenGLContext QOpenGLContextGroup QOpenGLPaintDevice
+ QOpenGLExtraFunctions QOpenGLFunctions
+ QRhiGles2InitParams QRhiGles2NativeHandles)
+endif()
+
set(QtGui_private_include_dirs
${Qt${QT_MAJOR_VERSION}Core_PRIVATE_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS})
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtGui">
+<typesystem package="PySide6.QtGui"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<?if windows?>
<load-typesystem name="QtGui/typesystem_gui_win.xml" generate="yes"/>
<object-type name="QAccessibleEditableTextInterface"/>
<object-type name="QAccessibleInterface"/>
<object-type name="QAccessibleObject" qt-register-metatype="base"/>
+ <object-type name="QAccessibleSelectionInterface" since="6.7"/>
<object-type name="QAccessibleTableCellInterface"/>
<object-type name="QAccessibleTextInterface"/>
<object-type name="QAccessibleValueInterface"/>
- <object-type name="QAccessibleEvent" copyable="false" polymorphic-id-expression="%1->type() == QAccessible::InvalidEvent"/>
- <object-type name="QAccessibleStateChangeEvent" copyable="false" polymorphic-id-expression="%1->type() == QAccessible::StateChanged"/>
- <object-type name="QAccessibleTableModelChangeEvent" copyable="false" polymorphic-id-expression="%1->type() == QAccessible::TableModelChanged">
+ <object-type name="QAccessibleEvent"
+ polymorphic-id-expression="%B->type() == QAccessible::InvalidEvent"/>
+ <object-type name="QAccessibleStateChangeEvent"
+ polymorphic-id-expression="%B->type() == QAccessible::StateChanged"/>
+ <object-type name="QAccessibleTableModelChangeEvent"
+ polymorphic-id-expression="%B->type() == QAccessible::TableModelChanged">
<enum-type name="ModelChangeType"/>
</object-type>
- <object-type name="QAccessibleTextCursorEvent" copyable="false" polymorphic-id-expression="%1->type() == QAccessible::TextCaretMoved"/>
- <object-type name="QAccessibleTextInsertEvent" copyable="false" polymorphic-id-expression="%1->type() == QAccessible::TextInserted"/>
- <object-type name="QAccessibleTextRemoveEvent" copyable="false" polymorphic-id-expression="%1->type() == QAccessible::TextRemoved"/>
- <object-type name="QAccessibleTextSelectionEvent" copyable="false" polymorphic-id-expression="%1->type() == QAccessible::TextSelectionChanged"/>
- <object-type name="QAccessibleTextUpdateEvent" copyable="false" polymorphic-id-expression="%1->type() == QAccessible::TextUpdated"/>
- <object-type name="QAccessibleValueChangeEvent" copyable="false" polymorphic-id-expression="%1->type() == QAccessible::ValueChanged"/>
+ <object-type name="QAccessibleTextCursorEvent"
+ polymorphic-id-expression="%B->type() == QAccessible::TextCaretMoved"/>
+ <object-type name="QAccessibleTextInsertEvent"
+ polymorphic-id-expression="%B->type() == QAccessible::TextInserted"/>
+ <object-type name="QAccessibleTextRemoveEvent"
+ polymorphic-id-expression="%B->type() == QAccessible::TextRemoved"/>
+ <object-type name="QAccessibleTextSelectionEvent"
+ polymorphic-id-expression="%B->type() == QAccessible::TextSelectionChanged"/>
+ <object-type name="QAccessibleTextUpdateEvent"
+ polymorphic-id-expression="%B->type() == QAccessible::TextUpdated"/>
+ <object-type name="QAccessibleValueChangeEvent"
+ polymorphic-id-expression="%B->type() == QAccessible::ValueChanged"/>
<object-type name="QAction">
<enum-type name="ActionEvent"/>
<value-type name="QIcon" >
<enum-type name="Mode"/>
<enum-type name="State"/>
+ <enum-type name="ThemeIcon" since="6.7"/>
<modify-function signature="QIcon(QIconEngine*)">
<modify-argument index="1">
<no-null-pointer/>
</modify-function>
</value-type>
- <value-type name="QConicalGradient" polymorphic-id-expression="%1->type() == QGradient::ConicalGradient"/>
+ <value-type name="QConicalGradient" polymorphic-id-expression="%B->type() == QGradient::ConicalGradient"/>
<value-type name="QFontInfo"/>
- <value-type name="QRadialGradient" polymorphic-id-expression="%1->type() == QGradient::RadialGradient"/>
+ <value-type name="QRadialGradient" polymorphic-id-expression="%B->type() == QGradient::RadialGradient"/>
<value-type name="QFont" >
<enum-type name="Capitalization"/>
<enum-type name="SpacingType"/>
<extra-includes>
<include file-name="QStringList" location="global"/>
</extra-includes>
+ <value-type name="Tag" since="6.7"/>
<!-- PYSIDE-1685: QFont(QString) should be checked first, else it will be interpreted as sequence -->
<modify-function signature="QFont(const QString&,int,int, bool)" overload-number="0"/>
<modify-function signature="QFont(const QStringList &,int,int, bool)" overload-number="1"/>
<inject-code class="target" position="beginning" file="../glue/qtgui.cpp" snippet="qfontmetrics-size"/>
</modify-function>
</value-type>
- <value-type name="QGradient" polymorphic-id-expression="%1->type() == QGradient::NoGradient">
+ <value-type name="QGradient" polymorphic-id-expression="%B->type() == QGradient::NoGradient">
<enum-type name="CoordinateMode"/>
<enum-type name="InterpolationMode"/>
<enum-type name="Preset"/>
<enum-type name="Spread"/>
<enum-type name="Type"/>
</value-type>
- <value-type name="QLinearGradient" polymorphic-id-expression="%1->type() == QGradient::LinearGradient"/>
+ <value-type name="QLinearGradient" polymorphic-id-expression="%B->type() == QGradient::LinearGradient"/>
<object-type name="QPaintDevice">
<enum-type name="PaintDeviceMetric"/>
</object-type>
</modify-function>
</object-type>
- <object-type name="QActionEvent" polymorphic-id-expression="%1->type() == QEvent::ActionAdded || %1->type() == QEvent::ActionRemoved || %1->type() == QEvent::ActionChanged"/>
- <object-type name="QCloseEvent" polymorphic-id-expression="%1->type() == QEvent::Close"/>
- <object-type name="QContextMenuEvent" polymorphic-id-expression="%1->type() == QEvent::ContextMenu">
+ <object-type name="QActionEvent"
+ polymorphic-id-expression="%B->type() == QEvent::ActionAdded || %B->type() == QEvent::ActionRemoved || %B->type() == QEvent::ActionChanged"/>
+ <object-type name="QCloseEvent" polymorphic-id-expression="%B->type() == QEvent::Close"/>
+ <object-type name="QContextMenuEvent" polymorphic-id-expression="%B->type() == QEvent::ContextMenu">
<enum-type name="Reason"/>
</object-type>
<value-type name="QEventPoint">
<enum-type name="State"/>
</value-type>
- <object-type name="QDragEnterEvent" polymorphic-id-expression="%1->type() == QEvent::DragEnter">
+ <object-type name="QDragEnterEvent" polymorphic-id-expression="%B->type() == QEvent::DragEnter">
<add-function signature="__repr__" return-type="PyObject">
<inject-code class="target" position="beginning">
<insert-template name="repr_qdebug_gui"/>
</inject-code>
</add-function>
</object-type>
- <object-type name="QDragLeaveEvent" polymorphic-id-expression="%1->type() == QEvent::DragLeave">
+ <object-type name="QDragLeaveEvent" polymorphic-id-expression="%B->type() == QEvent::DragLeave">
<add-function signature="__repr__" return-type="PyObject">
<inject-code class="target" position="beginning">
<insert-template name="repr_qdebug_gui"/>
</inject-code>
</add-function>
</object-type>
- <object-type name="QDragMoveEvent" polymorphic-id-expression="%1->type() == QEvent::DragMove">
+ <object-type name="QDragMoveEvent" polymorphic-id-expression="%B->type() == QEvent::DragMove">
<add-function signature="__repr__" return-type="PyObject">
<inject-code class="target" position="beginning">
<insert-template name="repr_qdebug_gui"/>
</inject-code>
</add-function>
</object-type>
- <object-type name="QDropEvent" polymorphic-id-expression="%1->type() == QEvent::Drop">
+ <object-type name="QDropEvent" polymorphic-id-expression="%B->type() == QEvent::Drop">
<modify-function signature="source()const">
<modify-argument index="return">
<define-ownership class="target" owner="default"/>
</inject-code>
</add-function>
</object-type>
- <object-type name="QEnterEvent" copyable="false" polymorphic-id-expression="%1->type() == QEvent::Enter"/>
- <object-type name="QExposeEvent" polymorphic-id-expression="%1->type() == QEvent::Expose"/>
- <object-type name="QFileOpenEvent" polymorphic-id-expression="%1->type() == QEvent::FileOpen"/>
- <object-type name="QFocusEvent" polymorphic-id-expression="%1->type() == QEvent::FocusIn || %1->type() == QEvent::FocusOut">
+ <object-type name="QChildWindowEvent" since="6.7"
+ polymorphic-id-expression="%B->type() == QEvent::ChildWindowAdded || %B->type() == QEvent::ChildWindowRemoved"/>
+ <object-type name="QEnterEvent"
+ polymorphic-id-expression="%B->type() == QEvent::Enter"/>
+ <object-type name="QExposeEvent" polymorphic-id-expression="%B->type() == QEvent::Expose"/>
+ <object-type name="QFileOpenEvent" polymorphic-id-expression="%B->type() == QEvent::FileOpen"/>
+ <object-type name="QFocusEvent"
+ polymorphic-id-expression="%B->type() == QEvent::FocusIn || %B->type() == QEvent::FocusOut">
<add-function signature="__repr__" return-type="PyObject">
<inject-code class="target" position="beginning">
<insert-template name="repr_qdebug_gui"/>
</inject-code>
</add-function>
</object-type>
- <object-type name="QHelpEvent" copyable="false" polymorphic-id-expression="%1->type() == QEvent::ToolTip || %1->type() == QEvent::WhatsThis"/>
- <object-type name="QHideEvent" copyable="false" polymorphic-id-expression="%1->type() == QEvent::Hide"/>
- <object-type name="QHoverEvent" copyable="false" polymorphic-id-expression="%1->type() == QEvent::HoverEnter || %1->type() == QEvent::HoverLeave || %1->type() == QEvent::HoverMove"/>
- <object-type name="QIconDragEvent" copyable="false" polymorphic-id-expression="%1->type() == QEvent::IconDrag"/>
+ <object-type name="QHelpEvent"
+ polymorphic-id-expression="%B->type() == QEvent::ToolTip || %B->type() == QEvent::WhatsThis"/>
+ <object-type name="QHideEvent" polymorphic-id-expression="%B->type() == QEvent::Hide"/>
+ <object-type name="QHoverEvent"
+ polymorphic-id-expression="%B->type() == QEvent::HoverEnter || %B->type() == QEvent::HoverLeave || %B->type() == QEvent::HoverMove"/>
+ <object-type name="QIconDragEvent" polymorphic-id-expression="%B->type() == QEvent::IconDrag"/>
- <object-type name="QInputMethodEvent" copyable="no" polymorphic-id-expression="%1->type() == QEvent::InputMethod">
+ <object-type name="QInputMethodEvent"
+ polymorphic-id-expression="%B->type() == QEvent::InputMethod">
<!-- only declare this if ndef QT_NO_INPUTMETHOD -->
<enum-type name="AttributeType"/>
<value-type name="Attribute">
</add-function>
<!-- endif ndef QT_NO_INPUTMETHOD -->
</object-type>
- <object-type name="QInputMethodQueryEvent" copyable="false" polymorphic-id-expression="%1->type() == QEvent::InputMethodQuery"/>
+ <object-type name="QInputMethodQueryEvent"
+ polymorphic-id-expression="%B->type() == QEvent::InputMethodQuery"/>
- <object-type name="QMoveEvent" copyable = "false" polymorphic-id-expression="%1->type() == QEvent::Move">
+ <object-type name="QMoveEvent" polymorphic-id-expression="%B->type() == QEvent::Move">
<add-function signature="__repr__" return-type="PyObject">
<inject-code class="target" position="beginning">
<insert-template name="repr_qdebug_gui"/>
</inject-code>
</add-function>
</object-type>
- <object-type name="QNativeGestureEvent" copyable="false" polymorphic-id-expression="%1->type() == QEvent::NativeGesture"/>
- <object-type name="QPlatformSurfaceEvent" copyable="false"
- polymorphic-id-expression="%1->type() == QEvent::PlatformSurface">
+ <object-type name="QNativeGestureEvent"
+ polymorphic-id-expression="%B->type() == QEvent::NativeGesture"/>
+ <object-type name="QPlatformSurfaceEvent"
+ polymorphic-id-expression="%B->type() == QEvent::PlatformSurface">
<enum-type name="SurfaceEventType"/>
</object-type>
- <object-type name="QResizeEvent" copyable = "false" polymorphic-id-expression="%1->type() == QEvent::Resize">
+ <object-type name="QResizeEvent" polymorphic-id-expression="%B->type() == QEvent::Resize">
<add-function signature="__repr__" return-type="PyObject">
<inject-code class="target" position="beginning">
<insert-template name="repr_qdebug_gui"/>
</inject-code>
</add-function>
</object-type>
- <object-type name="QShortcutEvent" copyable = "false" polymorphic-id-expression="%1->type() == QEvent::Shortcut">
+ <object-type name="QShortcutEvent"
+ polymorphic-id-expression="%B->type() == QEvent::Shortcut">
</object-type>
- <object-type name="QShowEvent" copyable= "false" polymorphic-id-expression="%1->type() == QEvent::Show"/>
- <object-type name="QSinglePointEvent" copyable="false"/>
- <object-type name="QStatusTipEvent" copyable= "false" polymorphic-id-expression="%1->type() == QEvent::StatusTip"/>
- <object-type name="QTabletEvent" copyable= "false" polymorphic-id-expression="%1->type() == QEvent::TabletMove || %1->type() == QEvent::TabletPress || %1->type() == QEvent::TabletRelease">
+ <object-type name="QShowEvent"
+ polymorphic-id-expression="%B->type() == QEvent::Show"/>
+ <object-type name="QSinglePointEvent"/>
+ <object-type name="QStatusTipEvent"
+ polymorphic-id-expression="%B->type() == QEvent::StatusTip"/>
+ <object-type name="QTabletEvent"
+ polymorphic-id-expression="%B->type() == QEvent::TabletMove || %B->type() == QEvent::TabletPress || %B->type() == QEvent::TabletRelease">
<add-function signature="__repr__" return-type="PyObject">
<inject-code class="target" position="beginning">
<insert-template name="repr_qdebug_gui"/>
</inject-code>
</add-function>
</object-type>
- <object-type name="QToolBarChangeEvent" polymorphic-id-expression="%1->type() == QEvent::ToolBarChange"/>
- <object-type name="QWhatsThisClickedEvent" copyable="false" polymorphic-id-expression="%1->type() == QEvent::WhatsThisClicked"/>
- <object-type name="QWheelEvent" copyable= "false" polymorphic-id-expression="%1->type() == QEvent::Wheel">
+ <object-type name="QToolBarChangeEvent"
+ polymorphic-id-expression="%B->type() == QEvent::ToolBarChange"/>
+ <object-type name="QWhatsThisClickedEvent"
+ polymorphic-id-expression="%B->type() == QEvent::WhatsThisClicked"/>
+ <object-type name="QWheelEvent" polymorphic-id-expression="%B->type() == QEvent::Wheel">
<add-function signature="__repr__" return-type="PyObject">
<inject-code class="target" position="beginning">
<insert-template name="repr_qdebug_gui"/>
</add-function>
</object-type>
- <object-type name="QWindowStateChangeEvent" copyable="false" polymorphic-id-expression="%1->type() == QEvent::WindowStateChange">
+ <object-type name="QWindowStateChangeEvent"
+ polymorphic-id-expression="%B->type() == QEvent::WindowStateChange">
<add-function signature="__repr__" return-type="PyObject">
<inject-code class="target" position="beginning">
<insert-template name="repr_qdebug_gui"/>
</inject-code>
</add-function>
</object-type>
- <object-type name="QInputEvent" copyable="false"/>
- <object-type name="QKeyEvent" copyable= "false" polymorphic-id-expression="%1->type() == QEvent::KeyPress || %1->type() == QEvent::KeyRelease || %1->type() == QEvent::ShortcutOverride">
+ <object-type name="QInputEvent"/>
+ <object-type name="QKeyEvent"
+ polymorphic-id-expression="%B->type() == QEvent::KeyPress || %B->type() == QEvent::KeyRelease || %B->type() == QEvent::ShortcutOverride">
<add-function signature="operator!=(QKeySequence::StandardKey)">
<inject-code class="target" file="../glue/qtgui.cpp" snippet="qkeyevent-operatornotequal"/>
</add-function>
</inject-code>
</add-function>
</object-type>
- <object-type name="QMouseEvent" copyable= "false"
- polymorphic-id-expression="%1->type() == QEvent::MouseButtonDblClick || %1->type() == QEvent::MouseButtonPress || %1->type() == QEvent::MouseButtonRelease || %1->type() == QEvent::MouseMove">
+ <object-type name="QMouseEvent"
+ polymorphic-id-expression="%B->type() == QEvent::MouseButtonDblClick || %B->type() == QEvent::MouseButtonPress || %B->type() == QEvent::MouseButtonRelease || %B->type() == QEvent::MouseMove">
<modify-function signature="globalPos() const" deprecated="yes"/>
<modify-function signature="localPos() const" deprecated="yes"/>
<modify-function signature="pos() const" deprecated="yes"/>
</inject-code>
</add-function>
</object-type>
- <object-type name="QPaintEvent" copyable= "false" polymorphic-id-expression="%1->type() == QEvent::Paint"/>
- <object-type name="QScrollEvent" copyable="false" polymorphic-id-expression="%1->type() == QEvent::Scroll">
+ <object-type name="QPaintEvent" polymorphic-id-expression="%B->type() == QEvent::Paint"/>
+ <object-type name="QScrollEvent" polymorphic-id-expression="%B->type() == QEvent::Scroll">
<enum-type name="ScrollState"/>
</object-type>
<object-type name="QPointerEvent" copyable= "false">
</inject-code>
</add-function>
</object-type>
- <object-type name="QScrollPrepareEvent" copyable="false" polymorphic-id-expression="%1->type() == QEvent::ScrollPrepare"/>
+ <object-type name="QScrollPrepareEvent"
+ polymorphic-id-expression="%B->type() == QEvent::ScrollPrepare"/>
<object-type name="QTextFrame" >
<extra-includes>
</add-function>
</object-type>
- <object-type name="QStandardItemModel" polymorphic-id-expression="qobject_cast<QStandardItemModel*>(%1)">
+ <object-type name="QStandardItemModel" polymorphic-id-expression="qobject_cast<QStandardItemModel*>(%B)">
<extra-includes>
<include file-name="QStringList" location="global"/>
<include file-name="QSize" location="global"/>
</inject-code>
</add-function>
- <!-- Qt5.5: XXX support the output variables! For now, I just suppressed the new methods. -->
- <modify-function signature="getAxisAndAngle(float*,float*,float*,float*)const"/>
- <modify-function signature="getAxisAndAngle(QVector3D*,float*)const"/>
- <modify-function signature="getEulerAngles(float*,float*,float*)const"/>
+ <!-- There can be only one return type. -->
+ <modify-function signature="getAxisAndAngle(float*,float*,float*,float*)const" remove="all"/>
+ <modify-function signature="getAxisAndAngle(QVector3D*,float*)const">
+ <modify-argument index="return" pyi-type="Tuple[PySide6.QtGui.QVector3D, float]">
+ <replace-type modified-type="(QVector3D, float)"/>
+ </modify-argument>
+ <modify-argument index="1"><remove-argument/></modify-argument>
+ <modify-argument index="2"><remove-argument/></modify-argument>
+ <inject-code class="target" position="beginning"
+ file="../glue/qtgui.cpp" snippet="qquaternion-getaxisandangle-vector3d-float"/>
+ </modify-function>
+ <modify-function signature="getEulerAngles(float*,float*,float*)const">
+ <modify-argument index="return" pyi-type="Tuple[float, float, float]">
+ <replace-type modified-type="(float, float, float)"/>
+ </modify-argument>
+ <modify-argument index="1"><remove-argument/></modify-argument>
+ <modify-argument index="2"><remove-argument/></modify-argument>
+ <modify-argument index="3"><remove-argument/></modify-argument>
+ <inject-code class="target" position="beginning"
+ file="../glue/qtgui.cpp" snippet="qquaternion-geteulerangles"/>
+ </modify-function>
</value-type>
<object-type name="QTouchEvent" since="4.6">
<add-function signature="exec_()" return-type="int">
<inject-code file="../glue/qtgui.cpp" snippet="qguiapplication-exec"/>
</add-function>
+ <add-function signature="nativeInterface()const" return-type="PyObject">
+ <modify-argument index="return"> <!-- Suppress return value heuristics -->
+ <define-ownership class="target" owner="default"/>
+ </modify-argument>
+ <inject-code class="target" position="beginning" file="../glue/qtgui.cpp"
+ snippet="qguiapplication-nativeInterface"/>
+ </add-function>
<modify-function signature="setOverrideCursor(const QCursor&)">
<modify-argument index="return" pyi-type="PyObject">
<replace-type modified-type="QtGuiHelper::QOverrideCursorGuard*"/>
</modify-function>
</object-type>
+ <namespace-type name="QNativeInterface" private="yes" since="6.7">
+ <object-type name="QX11Application" private="yes" disable-wrapper="yes"
+ force-abstract="yes">
+ <configuration condition="QT_CONFIG(xcb)"/>
+ <modify-function signature="display()const">
+ <modify-argument index="return">
+ <replace-type modified-type="int"/>
+ </modify-argument>
+ <inject-code class="target" position="end" file="../glue/qtgui.cpp"
+ snippet="qx11application-resource-ptr"/>
+ </modify-function>
+ <modify-function signature="connection()const">
+ <modify-argument index="return">
+ <replace-type modified-type="int"/>
+ </modify-argument>
+ <inject-code class="target" position="end" file="../glue/qtgui.cpp"
+ snippet="qx11application-resource-ptr"/>
+ </modify-function>
+ </object-type>
+ <object-type name="QWindowsScreen" private="yes" disable-wrapper="yes"
+ force-abstract="yes">
+ <configuration condition="#ifdef Q_OS_WIN"/>
+ </object-type>
+ </namespace-type>
+
<object-type name="QOpenGLContext">
<enum-type name="OpenGLModuleType"/>
</object-type>
</modify-argument>
<inject-code file="../glue/qtgui.cpp" snippet="qscreen-grabWindow"/>
</modify-function>
+ <add-function signature="nativeInterface()const" return-type="PyObject">
+ <modify-argument index="return"> <!-- Suppress return value heuristics -->
+ <define-ownership class="target" owner="default"/>
+ </modify-argument>
+ <inject-code class="target" position="beginning" file="../glue/qtgui.cpp"
+ snippet="qscreen-nativeInterface"/>
+ </add-function>
</object-type>
<object-type name="QStyleHints"/>
<enum-type name="CompareOp"/>
<enum-type name="StencilOp"/>
<enum-type name="PolygonMode"/>
+ <value-type name="StencilOpState" private="yes"/>
<value-type name="TargetBlend" private="yes"/>
<add-function signature="setShaderStages(QList<QRhiShaderStage>@stages@)">
<inject-code class="target" position="beginning"
</target-to-native>
</conversion-rule>
</primitive-type>
+ <primitive-type name="HMONITOR" target-lang-api-name="PyLong">
+ <conversion-rule>
+ <native-to-target file="../glue/qtgui.cpp" snippet="return-pylong-voidptr"/>
+ <target-to-native>
+ <add-conversion type="PyLong" file="../glue/qtgui.cpp"
+ snippet="conversion-pylong"/>
+ </target-to-native>
+ </conversion-rule>
+ </primitive-type>
<primitive-type name="HRGN" target-lang-api-name="PyLong">
<conversion-rule>
<native-to-target file="../glue/qtgui.cpp" snippet="return-pylong-voidptr"/>
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtGui"/>
+<typesystem package="PySide6.QtGui">
+ <custom-type name="_XDisplay"/>
+ <custom-type name="xcb_connection_t"/>
+</typesystem>
${QtHelp_GEN_DIR}/qhelpcontentitem_wrapper.cpp
${QtHelp_GEN_DIR}/qhelpcontentmodel_wrapper.cpp
${QtHelp_GEN_DIR}/qhelpcontentwidget_wrapper.cpp
+${QtHelp_GEN_DIR}/qhelpglobal_wrapper.cpp
${QtHelp_GEN_DIR}/qhelpengine_wrapper.cpp
${QtHelp_GEN_DIR}/qhelpenginecore_wrapper.cpp
${QtHelp_GEN_DIR}/qhelpfilterdata_wrapper.cpp
${QtCore_GEN_DIR}
)
set(QtHelp_libraries pyside6
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Gui_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Widgets_LIBRARIES}
${Qt${QT_MAJOR_VERSION}Help_LIBRARIES})
set(QtHelp_deps QtWidgets)
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtHelp">
+<typesystem package="PySide6.QtHelp"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtWidgets/typesystem_widgets.xml" generate="no"/>
<value-type name="QCompressedHelpInfo"/>
</modify-argument>
</modify-function>
</value-type>
- <object-type name="QHelpContentModel" polymorphic-id-expression="qobject_cast<QHelpContentModel*>(%1)"/>
+ <object-type name="QHelpContentModel" polymorphic-id-expression="qobject_cast<QHelpContentModel*>(%B)"/>
<object-type name="QHelpContentWidget"/>
+ <value-type name="QHelpGlobal"/>
<object-type name="QHelpEngine"/>
<object-type name="QHelpEngineCore"/>
<value-type name="QHelpFilterData"/>
${QtWebSockets_GEN_DIR})
set(QtHttpServer_libraries pyside6
- ${Qt${QT_MAJOR_VERSION}HttpServer_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Concurrent_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Network_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}WebSockets_LIBRARIES})
+ ${Qt${QT_MAJOR_VERSION}HttpServer_LIBRARIES})
set(QtHttpServer_deps QtCore QtConcurrent QtNetwork QtWebSockets)
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtHttpServer">
+<typesystem package="PySide6.QtHttpServer"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtConcurrent/typesystem_concurrent.xml" generate="no"/>
<load-typesystem name="QtWebSockets/typesystem_websockets.xml" generate="no"/>
// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtLocation">
+<typesystem package="PySide6.QtLocation"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<load-typesystem name="QtPositioning/typesystem_positioning.xml" generate="no"/>
<object-type name="QGeoCodeReply">
${QtMultimedia_GEN_DIR}/qmediatimerange_interval_wrapper.cpp
${QtMultimedia_GEN_DIR}/qscreencapture_wrapper.cpp
${QtMultimedia_GEN_DIR}/qsoundeffect_wrapper.cpp
+${QtMultimedia_GEN_DIR}/qtvideo_wrapper.cpp
${QtMultimedia_GEN_DIR}/qvideoframe_wrapper.cpp
+${QtMultimedia_GEN_DIR}/qvideoframe_paintoptions_wrapper.cpp
${QtMultimedia_GEN_DIR}/qvideoframeformat_wrapper.cpp
${QtMultimedia_GEN_DIR}/qvideosink_wrapper.cpp
+${QtMultimedia_GEN_DIR}/qwavedecoder_wrapper.cpp
${QtMultimedia_GEN_DIR}/qwindowcapture_wrapper.cpp
# module is always needed
${QtNetwork_GEN_DIR})
set(QtMultimedia_libraries pyside6
- ${Qt${QT_MAJOR_VERSION}Multimedia_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Gui_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Network_LIBRARIES}
- )
+ ${Qt${QT_MAJOR_VERSION}Multimedia_LIBRARIES})
+
set(QtMultimedia_deps QtCore QtGui QtNetwork)
create_pyside_module(NAME QtMultimedia
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtMultimedia">
+<typesystem package="PySide6.QtMultimedia"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/>
<load-typesystem name="QtNetwork/typesystem_network.xml" generate="no"/>
<enum-type name="Error"/>
<enum-type name="State"/>
<enum-type name="VolumeScale"/>
+ <inject-code class="target" position="end"
+ file="../glue/qtmultimedia.cpp"
+ snippet="qtaudio-namespace-compatibility-alias"/>
+ </namespace-type>
+
+ <namespace-type name="QtVideo" since="6.7">
+ <enum-type name="Rotation"/>
</namespace-type>
<value-type name="QAudioBuffer">
<object-type name="QAudioOutput"/>
<object-type name="QAudioSource">
- <modify-function signature="start()">
+ <modify-function signature="start()" allow-thread="true">
<modify-argument index="return">
<define-ownership class="target" owner="c++"/>
</modify-argument>
</modify-function>
- <modify-function signature="start(QIODevice*)">
+ <modify-function signature="start(QIODevice*)" allow-thread="true">
<modify-argument index="1">
<define-ownership class="target" owner="c++"/>
</modify-argument>
</modify-function>
+ <modify-function signature="stop()" allow-thread="true"/>
</object-type>
<object-type name="QAudioSink">
- <modify-function signature="start()">
+ <modify-function signature="start()" allow-thread="true">
<modify-argument index="return">
<define-ownership class="target" owner="c++"/>
</modify-argument>
</modify-function>
- <modify-function signature="start(QIODevice*)">
+ <modify-function signature="start(QIODevice*)" allow-thread="true">
<modify-argument index="1">
<define-ownership class="target" owner="c++"/>
</modify-argument>
</modify-function>
+ <modify-function signature="stop()" allow-thread="true"/>
</object-type>
<object-type name="QCamera">
<inject-code file="../glue/qtmultimedia.cpp" snippet="qvideoframe-bits"/>
</modify-function>
<modify-function signature="bits(int)const" remove="all"/>
+ <value-type name="PaintOptions">
+ <enum-type name="PaintFlag" flags="PaintFlags"/>
+ </value-type>
</value-type>
<value-type name="QVideoFrameFormat" since="6.1">
<enum-type name="ColorSpace" since="6.4"/>
<enum-type name="YCbCrColorSpace"/>
</value-type>
+ <object-type name="QWaveDecoder">
+ <!-- No implementation -->
+ <modify-function signature="setIODevice(QIODevice*)" remove="all"/>
+ </object-type>
+
<object-type name="QWindowCapture" since="6.6">
<enum-type name="Error"/>
</object-type>
${QtMultimedia_GEN_DIR})
set(QtMultimediaWidgets_libraries pyside6
- ${Qt${QT_MAJOR_VERSION}Multimedia_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}MultimediaWidgets_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Gui_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Network_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Widgets_LIBRARIES})
+ ${Qt${QT_MAJOR_VERSION}MultimediaWidgets_LIBRARIES})
+
set(QtMultimediaWidgets_deps QtCore QtGui QtNetwork QtWidgets QtMultimedia)
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtMultimediaWidgets">
+<typesystem package="PySide6.QtMultimediaWidgets"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/>
<load-typesystem name="QtMultimedia/typesystem_multimedia.xml" generate="no"/>
${QtNetwork_GEN_DIR}/qhostaddress_wrapper.cpp
${QtNetwork_GEN_DIR}/qhostinfo_wrapper.cpp
${QtNetwork_GEN_DIR}/qhstspolicy_wrapper.cpp
+${QtNetwork_GEN_DIR}/qhttp1configuration_wrapper.cpp
+${QtNetwork_GEN_DIR}/qhttpheaders_wrapper.cpp
${QtNetwork_GEN_DIR}/qhttpmultipart_wrapper.cpp
${QtNetwork_GEN_DIR}/qhttppart_wrapper.cpp
${QtNetwork_GEN_DIR}/qhttp2configuration_wrapper.cpp
${QtNetwork_GEN_DIR}/qnetworkproxyquery_wrapper.cpp
${QtNetwork_GEN_DIR}/qnetworkreply_wrapper.cpp
${QtNetwork_GEN_DIR}/qnetworkrequest_wrapper.cpp
+${QtNetwork_GEN_DIR}/qnetworkrequestfactory_wrapper.cpp
${QtNetwork_GEN_DIR}/qpassworddigestor_wrapper.cpp
+${QtNetwork_GEN_DIR}/qrestaccessmanager_wrapper.cpp
+${QtNetwork_GEN_DIR}/qrestreply_wrapper.cpp
${QtNetwork_GEN_DIR}/qssl_wrapper.cpp
${QtNetwork_GEN_DIR}/qsslcertificate_wrapper.cpp
${QtNetwork_GEN_DIR}/qsslcertificateextension_wrapper.cpp
else()
list(APPEND QtNetwork_SRC
${QtNetwork_GEN_DIR}/qdtls_wrapper.cpp
- ${QtNetwork_GEN_DIR}/qdtlsclientverifier_wrapper.cpp)
+ ${QtNetwork_GEN_DIR}/qdtlsclientverifier_wrapper.cpp
+ ${QtNetwork_GEN_DIR}/qdtlsclientverifier_generatorparameters_wrapper.cpp)
message(STATUS "Qt${QT_MAJOR_VERSION}Network: Adding DTLS classes")
endif()
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtNetwork">
+<typesystem package="PySide6.QtNetwork"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<rejection class="QTlsPrivate"/>
</object-type>
<object-type name="QDtlsClientVerifier">
<configuration condition="QT_CONFIG(dtls)"/>
+ <value-type name="GeneratorParameters">
+ <configuration condition="QT_CONFIG(dtls)"/>
+ </value-type>
</object-type>
<value-type name="QHstsPolicy">
<enum-type name="PolicyFlag" flags="PolicyFlags"/>
</value-type>
+ <value-type name="QHttp1Configuration"/>
+ <value-type name="QHttpHeaders" since="6.7">
+ <enum-type name="WellKnownHeader"/>
+ </value-type>
<object-type name="QHttpMultiPart">
<enum-type name="ContentType"/>
</object-type>
<modify-function signature="put(const QNetworkRequest &,QIODevice*)" allow-thread="yes"/>
<modify-function signature="put(const QNetworkRequest &,const QByteArray &)" allow-thread="yes"/>
<modify-function signature="sendCustomRequest(const QNetworkRequest &,const QByteArray &,QIODevice*)" allow-thread="yes" since="4.7"/>
+ <modify-function signature="setCache(QAbstractNetworkCache*)">
+ <modify-argument index="1">
+ <define-ownership class="target" owner="c++"/>
+ </modify-argument>
+ </modify-function>
<modify-function signature="setCookieJar(QNetworkCookieJar*)">
<modify-argument index="1">
<define-ownership class="target" owner="c++"/>
</value-type>
<value-type name="QHostInfo">
+ <inject-code class="native" position="beginning" file="../glue/qtnetwork.cpp"
+ snippet="qhostinfo-lookuphost-functor"/>
<enum-type name="HostInfoError"/>
<add-function signature="lookupHost(const QString &,PyCallable)">
<inject-code class="target" position="beginning"
</value-type>
<value-type name="QNetworkRequest">
<enum-type name="Attribute"/>
- <enum-type name="LoadControl" since="4.7"/>
- <enum-type name="Priority" since="4.7"/>
+ <enum-type name="LoadControl"/>
+ <enum-type name="Priority"/>
<enum-type name="CacheLoadControl"/>
<enum-type name="KnownHeaders"/>
<enum-type name="RedirectPolicy"/>
<enum-type name="TransferTimeoutConstant"/>
</value-type>
-
+ <value-type name="QNetworkRequestFactory" since="6.7"/>
<object-type name="QAbstractNetworkCache"/>
<object-type name="QNetworkDiskCache"/>
<value-type name="QNetworkCacheMetaData"/>
+ <object-type name="QRestAccessManager" since="6.7">
+ <inject-code class="native" position="beginning" file="../glue/qtnetwork.cpp"
+ snippet="qrestaccessmanager-functor"/>
+
+ <add-function signature="deleteResource(QNetworkRequest@request@,QObject*@context@,PyCallable*@slot@)"
+ return-type="QNetworkReply*">
+ <inject-code class="target" position="beginning" file="../glue/qtnetwork.cpp"
+ snippet="qrestaccessmanager-callback"/>
+ </add-function>
+
+ <add-function signature="get(QNetworkRequest@request@,QObject*@context@,PyCallable*@slot@)"
+ return-type="QNetworkReply*">
+ <inject-code class="target" position="beginning" file="../glue/qtnetwork.cpp"
+ snippet="qrestaccessmanager-callback"/>
+ </add-function>
+ <add-function signature="get(QNetworkRequest@request@,QByteArray@data@,QObject*@context@,PyCallable*@slot@)"
+ return-type="QNetworkReply*">
+ <inject-code class="target" position="beginning" file="../glue/qtnetwork.cpp"
+ snippet="qrestaccessmanager-data-callback"/>
+ </add-function>
+ <add-function signature="get(QNetworkRequest@request@,QIODevice*@data@,QObject*@context@,PyCallable*@slot@)"
+ return-type="QNetworkReply*">
+ <inject-code class="target" position="beginning" file="../glue/qtnetwork.cpp"
+ snippet="qrestaccessmanager-data-callback"/>
+ </add-function>
+ <add-function signature="get(QNetworkRequest@request@,QJsonDocument@data@,QObject*@context@,PyCallable*@slot@)"
+ return-type="QNetworkReply*">
+ <inject-code class="target" position="beginning" file="../glue/qtnetwork.cpp"
+ snippet="qrestaccessmanager-data-callback"/>
+ </add-function>
+
+ <add-function signature="head(QNetworkRequest@request@,QObject*@context@,PyCallable*@slot@)"
+ return-type="QNetworkReply*">
+ <inject-code class="target" position="beginning" file="../glue/qtnetwork.cpp"
+ snippet="qrestaccessmanager-callback"/>
+ </add-function>
+
+ <add-function signature="patch(QNetworkRequest@request@,QByteArray@data@,QObject*@context@,PyCallable*@slot@)"
+ return-type="QNetworkReply*">
+ <inject-code class="target" position="beginning" file="../glue/qtnetwork.cpp"
+ snippet="qrestaccessmanager-data-callback"/>
+ </add-function>
+ <add-function signature="patch(QNetworkRequest@request@,QIODevice*@data@,QObject*@context@,PyCallable*@slot@)"
+ return-type="QNetworkReply*">
+ <inject-code class="target" position="beginning" file="../glue/qtnetwork.cpp"
+ snippet="qrestaccessmanager-data-callback"/>
+ </add-function>
+ <add-function signature="patch(QNetworkRequest@request@,QJsonDocument@data@,QObject*@context@,PyCallable*@slot@)"
+ return-type="QNetworkReply*">
+ <inject-code class="target" position="beginning" file="../glue/qtnetwork.cpp"
+ snippet="qrestaccessmanager-data-callback"/>
+ </add-function>
+ <add-function signature="patch(QNetworkRequest@request@,QVariantMap@data@,QObject*@context@,PyCallable*@slot@)"
+ return-type="QNetworkReply*">
+ <inject-code class="target" position="beginning" file="../glue/qtnetwork.cpp"
+ snippet="qrestaccessmanager-data-callback"/>
+ </add-function>
+
+ <add-function signature="post(QNetworkRequest@request@,QByteArray@data@,QObject*@context@,PyCallable*@slot@)"
+ return-type="QNetworkReply*">
+ <inject-code class="target" position="beginning" file="../glue/qtnetwork.cpp"
+ snippet="qrestaccessmanager-data-callback"/>
+ </add-function>
+ <add-function signature="post(QNetworkRequest@request@,QHttpMultiPart*@data@,QObject*@context@,PyCallable*@slot@)"
+ return-type="QNetworkReply*">
+ <inject-code class="target" position="beginning" file="../glue/qtnetwork.cpp"
+ snippet="qrestaccessmanager-data-callback"/>
+ </add-function>
+ <add-function signature="post(QNetworkRequest@request@,QIODevice*@data@,QObject*@context@,PyCallable*@slot@)"
+ return-type="QNetworkReply*">
+ <inject-code class="target" position="beginning" file="../glue/qtnetwork.cpp"
+ snippet="qrestaccessmanager-data-callback"/>
+ </add-function>
+ <add-function signature="post(QNetworkRequest@request@,QJsonDocument@data@,QObject*@context@,PyCallable*@slot@)"
+ return-type="QNetworkReply*">
+ <inject-code class="target" position="beginning" file="../glue/qtnetwork.cpp"
+ snippet="qrestaccessmanager-data-callback"/>
+ </add-function>
+ <add-function signature="post(QNetworkRequest@request@,QVariantMap@data@,QObject*@context@,PyCallable*@slot@)"
+ return-type="QNetworkReply*">
+ <inject-code class="target" position="beginning" file="../glue/qtnetwork.cpp"
+ snippet="qrestaccessmanager-data-callback"/>
+ </add-function>
+
+ <add-function signature="put(QNetworkRequest@request@,QByteArray@data@,QObject*@context@,PyCallable*@slot@)"
+ return-type="QNetworkReply*">
+ <inject-code class="target" position="beginning" file="../glue/qtnetwork.cpp"
+ snippet="qrestaccessmanager-data-callback"/>
+ </add-function>
+ <add-function signature="put(QNetworkRequest@request@,QHttpMultiPart*@data@,QObject*@context@,PyCallable*@slot@)"
+ return-type="QNetworkReply*">
+ <inject-code class="target" position="beginning" file="../glue/qtnetwork.cpp"
+ snippet="qrestaccessmanager-data-callback"/>
+ </add-function>
+ <add-function signature="put(QNetworkRequest@request@,QIODevice*@data@,QObject*@context@,PyCallable*@slot@)"
+ return-type="QNetworkReply*">
+ <inject-code class="target" position="beginning" file="../glue/qtnetwork.cpp"
+ snippet="qrestaccessmanager-data-callback"/>
+ </add-function>
+ <add-function signature="put(QNetworkRequest@request@,QJsonDocument@data@,QObject*@context@,PyCallable*@slot@)"
+ return-type="QNetworkReply*">
+ <inject-code class="target" position="beginning" file="../glue/qtnetwork.cpp"
+ snippet="qrestaccessmanager-data-callback"/>
+ </add-function>
+ <add-function signature="put(QNetworkRequest@request@,QVariantMap@data@,QObject*@context@,PyCallable*@slot@)"
+ return-type="QNetworkReply*">
+ <inject-code class="target" position="beginning" file="../glue/qtnetwork.cpp"
+ snippet="qrestaccessmanager-data-callback"/>
+ </add-function>
+
+ <add-function signature="sendCustomRequest(QNetworkRequest@request@,QByteArray@method@,QByteArray@data@,QObject*@context@,PyCallable*@slot@)"
+ return-type="QNetworkReply*">
+ <inject-code class="target" position="beginning" file="../glue/qtnetwork.cpp"
+ snippet="qrestaccessmanager-method-data-callback"/>
+ </add-function>
+ <add-function signature="sendCustomRequest(QNetworkRequest@request@,QByteArray@method@,QHttpMultiPart*@data@,QObject*@context@,PyCallable*@slot@)"
+ return-type="QNetworkReply*">
+ <inject-code class="target" position="beginning" file="../glue/qtnetwork.cpp"
+ snippet="qrestaccessmanager-method-data-callback"/>
+ </add-function>
+ <add-function signature="sendCustomRequest(QNetworkRequest@request@,QByteArray@method@,QIODevice*@data@,QObject*@context@,PyCallable*@slot@)"
+ return-type="QNetworkReply*">
+ <inject-code class="target" position="beginning" file="../glue/qtnetwork.cpp"
+ snippet="qrestaccessmanager-method-data-callback"/>
+ </add-function>
+ </object-type>
+
+ <object-type name="QRestReply" since="6.7">
+ <add-function signature="readJson()" return-type="PyObject">
+ <modify-argument index="return"
+ pyi-type="Tuple[Optional[PySide6.QtCore.QJsonDocument],PySide6.QtCore.QJsonParseError]"/>
+ <inject-code class="target" position="beginning" file="../glue/qtnetwork.cpp"
+ snippet="qrestreply-readjson"/>
+ </add-function>
+ </object-type>
+
<object-type name="QSctpServer">
<configuration condition="QT_CONFIG(sctp)"/>
</object-type>
${QtNetworkAuth_GEN_DIR})
set(QtNetworkAuth_libraries pyside6
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Network_LIBRARIES}
${Qt${QT_MAJOR_VERSION}NetworkAuth_LIBRARIES})
set(QtNetworkAuth_deps QtNetwork)
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtNetworkAuth">
+<typesystem package="PySide6.QtNetworkAuth"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtNetwork/typesystem_network.xml" generate="no"/>
<object-type name="QAbstractOAuth">
<enum-type name="ContentType"/>
<enum-type name="Error"/>
<enum-type name="Stage"/>
<enum-type name="Status"/>
+ <inject-code class="native" position="beginning" file="../glue/qtnetworkauth.cpp"
+ snippet="qabstractoauth-lookuphost-functor"/>
<modify-function signature="setReplyHandler(QAbstractOAuthReplyHandler*)">
<modify-argument index="1">
<define-ownership class="target" owner="c++"/>
${QtNfc_GEN_DIR}/qndefnfcurirecord_wrapper.cpp
${QtNfc_GEN_DIR}/qnearfieldmanager_wrapper.cpp
${QtNfc_GEN_DIR}/qnearfieldtarget_wrapper.cpp
+ ${QtNfc_GEN_DIR}/qnearfieldtarget_requestid_wrapper.cpp
# module is always needed
${QtNfc_GEN_DIR}/qtnfc_module_wrapper.cpp)
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtNfc">
+<typesystem package="PySide6.QtNfc"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<value-type name="QNdefFilter">
<value-type name="Record"/>
<enum-type name="AccessMethod" flags="AccessMethods"/>
<enum-type name="Error"/>
<enum-type name="Type"/>
+ <value-type name="RequestId"/>
</object-type>
<!-- QtNetwork is pulled in via QtNfcDepends. -->
<suppress-warning text="^Scoped enum 'Q(Ocsp)|(Dtls).*' does not have a type entry.*$"/>
${QtOpenGL_GEN_DIR}
)
set(QtOpenGL_libraries pyside6
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Gui_LIBRARIES}
${Qt${QT_MAJOR_VERSION}OpenGL_LIBRARIES})
set(QtOpenGL_deps QtGui)
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtOpenGL">
+<typesystem package="PySide6.QtOpenGL"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no" />
<load-typesystem name="QtGui/typesystem_gui.xml" generate="no" />
<load-typesystem name="templates/opengl_common.xml" generate="no" />
</object-type>
<value-type name="QOpenGLFramebufferObjectFormat"/>
<object-type name="QAbstractOpenGLFunctions"/>
- <value-type name="QOpenGLPixelTransferOptions"/>
+ <value-type name="QOpenGLPixelTransferOptions"/>
<object-type name="QOpenGLShader">
<enum-type name="ShaderTypeBit" flags="ShaderType"/>
</object-type>
${QtOpenGLWidgets_GEN_DIR})
set(QtOpenGLWidgets_libraries pyside6
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Gui_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}OpenGL_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Widgets_LIBRARIES}
${Qt${QT_MAJOR_VERSION}OpenGLWidgets_LIBRARIES})
set(QtOpenGLWidgets_deps QtOpenGL QtWidgets)
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtOpenGLWidgets">
+<typesystem package="PySide6.QtOpenGLWidgets"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no" />
<load-typesystem name="QtGui/typesystem_gui.xml" generate="no" />
<load-typesystem name="QtWidgets/typesystem_widgets.xml" generate="no" />
${QtNetwork_GEN_DIR})
set(QtPdf_libraries pyside6
- ${Qt${QT_MAJOR_VERSION}Pdf_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Gui_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Network_LIBRARIES})
+ ${Qt${QT_MAJOR_VERSION}Pdf_LIBRARIES})
set(QtPdf_deps QtCore QtGui QtNetwork)
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtPdf">
+<typesystem package="PySide6.QtPdf"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/>
<object-type name="QPdfBookmarkModel">
${QtPdf_GEN_DIR})
set(QtPdfWidgets_libraries pyside6
- ${Qt${QT_MAJOR_VERSION}Pdf_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}PdfWidgets_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Gui_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Network_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Widgets_LIBRARIES})
+ ${Qt${QT_MAJOR_VERSION}PdfWidgets_LIBRARIES})
set(QtPdfWidgets_deps QtCore QtGui QtNetwork QtWidgets QtPdf)
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtPdfWidgets">
+<typesystem package="PySide6.QtPdfWidgets"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/>
<load-typesystem name="QtWidgets/typesystem_widgets.xml" generate="no"/>
${QtPositioning_GEN_DIR}/qgeosatelliteinfosource_wrapper.cpp
${QtPositioning_GEN_DIR}/qgeoshape_wrapper.cpp
${QtPositioning_GEN_DIR}/qnmeapositioninfosource_wrapper.cpp
+${QtPositioning_GEN_DIR}/qnmeasatelliteinfosource_wrapper.cpp
# module is always needed
${QtPositioning_GEN_DIR}/qtpositioning_module_wrapper.cpp
)
TYPESYSTEM_PATH QtPositioning_SOURCE_DIR
SOURCES QtPositioning_SRC
DROPPED_ENTRIES QtPositioning_DROPPED_ENTRIES)
+
+if (APPLE)
+ # The QtLocation permission plugin cannot be linked to QtCore.abi3.so because for a framework
+ # build of Qt, the QtCore framework bundle must be loaded before calling
+ # Q_IMPORT_PLUGIN(QDarwinLocationPermissionPlugin)
+ set(permission_plugin_name "QDarwinLocationPermissionPlugin")
+ set(permission_plugin "${QT_CMAKE_EXPORT_NAMESPACE}::${permission_plugin_name}")
+ set_target_properties(QtPositioning PROPERTIES "_qt_has_${permission_plugin_name}_usage_description" TRUE)
+ # importing the plugin
+ qt6_import_plugins(QtPositioning INCLUDE ${permission_plugin})
+endif()
// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtPositioning">
+<typesystem package="PySide6.QtPositioning"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
+ <inject-code class="native" position="beginning" file="../glue/qtpositioning.cpp"
+ snippet="darwin_location_permission_plugin"/>
<value-type name="QGeoAddress"/>
<value-type name="QGeoAreaMonitorInfo"/>
<object-type name="QGeoAreaMonitorSource">
<object-type name="QNmeaPositionInfoSource">
<enum-type name="UpdateMode"/>
</object-type>
+ <object-type name="QNmeaSatelliteInfoSource">
+ <enum-type name="UpdateMode"/>
+ <enum-type name="SatelliteInfoParseStatus"/>
+ </object-type>
</typesystem>
${libpyside_SOURCE_DIR}
${QtCore_GEN_DIR}
${QtGui_GEN_DIR}
- ${QtWidgets_GEN_DIR}
- )
+ ${QtWidgets_GEN_DIR})
+
set(QtPrintSupport_libraries pyside6
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Gui_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Widgets_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}PrintSupport_LIBRARIES}
- )
+ ${Qt${QT_MAJOR_VERSION}PrintSupport_LIBRARIES})
+
set(QtPrintSupport_deps QtWidgets)
create_pyside_module(NAME QtPrintSupport
INCLUDE_DIRS QtPrintSupport_include_dirs
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtPrintSupport">
+<typesystem package="PySide6.QtPrintSupport"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtWidgets/typesystem_widgets.xml" generate="no"/>
<load-typesystem name="QtPrintSupport/typesystem_printsupport_common.xml" generate="yes"/>
</typesystem>
${QtQml_GEN_DIR})
set(QtQml_libraries pyside6 pyside6qml
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Network_LIBRARIES}
${Qt${QT_MAJOR_VERSION}Qml_LIBRARIES})
set(QtQml_deps QtNetwork)
#include "pysideqmlvolatilebool.h"
+#include <pep384ext.h>
#include <signature.h>
#include <QtCore/QDebug>
if (ok < 0)
return nullptr;
- QtQml_VolatileBoolObject *self
- = reinterpret_cast<QtQml_VolatileBoolObject *>(type->tp_alloc(type, 0));
+ auto *self = PepExt_TypeCallAlloc<QtQml_VolatileBoolObject>(type, 0);
if (self != nullptr)
self->flag = new AtomicBool(ok);
static PyObject *
QtQml_VolatileBoolObject_get(QtQml_VolatileBoolObject *self)
{
- return *self->flag ? Py_True : Py_False;
+ if (*self->flag) {
+ Py_RETURN_TRUE;
+ }
+ Py_RETURN_FALSE;
}
static PyObject *
}
ok = PyObject_IsTrue(value);
- if (ok < 0) {
- PyErr_SetString(PyExc_TypeError, "Not a boolean value.");
- return nullptr;
- }
+ if (ok < 0)
+ return PyErr_Format(PyExc_TypeError, "Not a boolean value.");
*self->flag = ok > 0;
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtQml">
+<typesystem package="PySide6.QtQml"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<load-typesystem name="QtNetwork/typesystem_network.xml" generate="no"/>
in generator tests folder. -->
</primitive-type>
- <enum-type name="QQmlModuleImportSpecialVersions"/>
+ <enum-type name="QQmlModuleImportSpecialVersions" doc-file="qqmlengine"/>
<!-- expose QQmlIncubationController::incubateWhile() (see
QtQml_VolatileBoolTypeF/pysideqmlvolatilebool.h) -->
<add-function signature="qmlRegisterType(PyTypeObject@type_obj@,const char*@uri@,int@version_major@,int@version_minor@,const char*@qml_name@)" return-type="int">
<inject-code class="target" file="../glue/qtqml.cpp" snippet="qmlregistertype"/>
+ <inject-documentation format="target" mode="append" file="../doc/qtqml_functions.rst"
+ snippet="qmlregistertype"/>
<modify-argument index="2" pyi-type="str"/>
</add-function>
<add-function signature="qmlRegisterSingletonType(PyTypeObject@type_obj@,const char*@uri@,int@version_major@,int@version_minor@,const char*@qml_name@,PyObject*@callback@)" return-type="int">
<inject-code class="target" file="../glue/qtqml.cpp" snippet="qmlregistersingletontype_qobject_callback"/>
+ <inject-documentation format="target" mode="append" file="../doc/qtqml_functions.rst"
+ snippet="qmlregistersingletontype_qobject_callback"/>
<modify-argument index="2" pyi-type="str"/>
</add-function>
<add-function signature="qmlRegisterSingletonType(PyTypeObject@type_obj@,const char*@uri@,int@version_major@,int@version_minor@,const char*@qml_name@)" return-type="int">
<inject-code class="target" file="../glue/qtqml.cpp" snippet="qmlregistersingletontype_qobject_nocallback"/>
+ <inject-documentation format="target" mode="append" file="../doc/qtqml_functions.rst"
+ snippet="qmlregistersingletontype_qobject_nocallback"/>
<modify-argument index="2" pyi-type="str"/>
</add-function>
<add-function signature="qmlRegisterSingletonType(const char*@uri@,int@version_major@,int@version_minor@,const char*@qml_name@,PyObject*@callback@)" return-type="int">
<inject-code class="target" file="../glue/qtqml.cpp" snippet="qmlregistersingletontype_qjsvalue"/>
+ <inject-documentation format="target" mode="append" file="../doc/qtqml_functions.rst"
+ snippet="qmlregistersingletontype_qjsvalue"/>
<modify-argument index="1" pyi-type="str"/>
</add-function>
<add-function signature="qmlRegisterSingletonInstance(PyTypeObject@type_obj@,const char*@uri@,int@version_major@,int@version_minor@,const char*@qml_name@,PyObject*@callback@)" return-type="int">
<inject-code class="target" file="../glue/qtqml.cpp" snippet="qmlregistersingletoninstance"/>
+ <inject-documentation format="target" mode="append" file="../doc/qtqml_functions.rst"
+ snippet="qmlregistersingletoninstance"/>
<modify-argument index="2" pyi-type="str"/>
</add-function>
<add-function signature="qmlRegisterUncreatableType(PyTypeObject@type_obj@,const char*@uri@,int@version_major@,int@version_minor@,const char*@qml_name@,const char*@message@)" return-type="int">
<inject-code class="target" file="../glue/qtqml.cpp" snippet="qmlregisteruncreatabletype"/>
+ <inject-documentation format="target" mode="append" file="../doc/qtqml_functions.rst"
+ snippet="qmlregisteruncreatabletype"/>
<modify-argument index="2" pyi-type="str"/>
</add-function>
<modify-argument index="return" pyi-type="Optional[PySide6.QtQml.QJSEngine]"/>
</modify-function>
</function>
- <function signature="qmlClearTypeRegistrations()"/>
+ <function signature="qmlClearTypeRegistrations()" doc-file="qqmlengine"/>
<function signature="qmlContext(const QObject*)">
<modify-function>
<modify-argument index="return" pyi-type="Optional[PySide6.QtQml.QQmlContext]"/>
<modify-argument index="return" pyi-type="Optional[PySide6.QtQml.QQmlEngine]"/>
</modify-function>
</function>
- <function signature="qmlProtectModule(const char*,int)"/>
- <function signature="qmlRegisterModule(const char*,int,int)"/>
- <function signature="qmlTypeId(const char*,int,int,const char*)"/>
- <function signature="qmlRegisterType(const QUrl &,const char *,int,int,const char *)"/>
- <function signature="qmlRegisterSingletonType(const QUrl &,const char *,int,int,const char *)"/>
- <function signature="qmlRegisterUncreatableMetaObject(const QMetaObject&,const char*,int,int, const char*,const QString&)"/>
+ <function signature="qmlProtectModule(const char*,int)" doc-file="qqmlengine"/>
+ <function signature="qmlRegisterModule(const char*,int,int)" doc-file="qqmlengine"/>
+ <function signature="qmlTypeId(const char*,int,int,const char*)" doc-file="qqmlengine"/>
+ <function signature="qmlRegisterType(const QUrl &,const char *,int,int,const char *)"
+ doc-file="qqmlengine"/>
+ <function signature="qmlRegisterSingletonType(const QUrl &,const char *,int,int,const char *)"
+ doc-file="qqmlengine"/>
+ <function signature="qmlRegisterUncreatableMetaObject(const QMetaObject&,const char*,int,int, const char*,const QString&)"
+ doc-file="qqmlengine"/>
<enum-type identified-by-value="QML_HAS_ATTACHED_PROPERTIES">
<extra-includes>
<modify-argument index="1"><replace-type modified-type="PyPathLike"/></modify-argument>
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qfile-path-1"/>
</modify-function>
+ <add-function signature="singletonInstance(int@qmlTypeId@)"
+ return-type="QObject*">
+ <!-- Suppress return value heuristics -->
+ <modify-argument index="return"
+ pyi-type="Union[PySide6.QtCore.QObject, PySide6.QtQml.QJSValue, None]">
+ <define-ownership class="target" owner="default"/>
+ </modify-argument>
+ <inject-code class="target" file="../glue/qtqml.cpp"
+ snippet="qqmlengine-singletoninstance-qmltypeid"/>
+ <inject-documentation format="target" mode="append" file="../doc/qtqml_functions.rst"
+ snippet="qqmlengine-singletoninstance-qmltypeid"/>
+ </add-function>
+ <add-function signature="singletonInstance(QString@uri@,QString@typeName@)"
+ return-type="QObject*">
+ <!-- Suppress return value heuristics -->
+ <modify-argument index="return"
+ pyi-type="Union[PySide6.QtCore.QObject, PySide6.QtQml.QJSValue, None]">
+ <define-ownership class="target" owner="default"/>
+ </modify-argument>
+ <inject-code class="target" file="../glue/qtqml.cpp"
+ snippet="qqmlengine-singletoninstance-typename"/>
+ <inject-documentation format="target" mode="append" file="../doc/qtqml_functions.rst"
+ snippet="qqmlengine-singletoninstance-typename"/>
+ </add-function>
</object-type>
<object-type name="QQmlExpression">
project(QtQuick)
+set(QtQuick_DROPPED_ENTRIES)
+
set(QtQuick_registerType "${QtQuick_SOURCE_DIR}/pysidequickregistertype.cpp")
# Exclude sources that have clashing static helper functions named "renderstate_..."
set(QtQuick_SRC
${QtQuick_SRC_UNITY_EXCLUDED_SRC}
${QtQuick_GEN_DIR}/qquickasyncimageprovider_wrapper.cpp
-${QtQuick_GEN_DIR}/qquickframebufferobject_wrapper.cpp
-${QtQuick_GEN_DIR}/qquickframebufferobject_renderer_wrapper.cpp
${QtQuick_GEN_DIR}/qquickgraphicsconfiguration_wrapper.cpp
${QtQuick_GEN_DIR}/qquickgraphicsdevice_wrapper.cpp
${QtQuick_GEN_DIR}/qquicktexturefactory_wrapper.cpp
${QtQuick_GEN_DIR}/qquickpainteditem_wrapper.cpp
${QtQuick_GEN_DIR}/qquickrendercontrol_wrapper.cpp
${QtQuick_GEN_DIR}/qquickrendertarget_wrapper.cpp
+${QtQuick_GEN_DIR}/qquickrhiitemrenderer_wrapper.cpp
+${QtQuick_GEN_DIR}/qquickrhiitem_wrapper.cpp
${QtQuick_GEN_DIR}/qquicktextdocument_wrapper.cpp
${QtQuick_GEN_DIR}/qquickview_wrapper.cpp
${QtQuick_GEN_DIR}/qquickwindow_wrapper.cpp
+${QtQuick_GEN_DIR}/qquickwindow_graphicsstateinfo_wrapper.cpp
${QtQuick_GEN_DIR}/qsgbasicgeometrynode_wrapper.cpp
${QtQuick_GEN_DIR}/qsgclipnode_wrapper.cpp
${QtQuick_GEN_DIR}/qsgdynamictexture_wrapper.cpp
${QtQuick_GEN_DIR}/qsggeometry_texturedpoint2d_wrapper.cpp
${QtQuick_GEN_DIR}/qsggeometry_wrapper.cpp
${QtQuick_GEN_DIR}/qsggeometrynode_wrapper.cpp
+${QtQuick_GEN_DIR}/qsgimagenode_wrapper.cpp
${QtQuick_GEN_DIR}/qsgmaterial_wrapper.cpp
${QtQuick_GEN_DIR}/qsgmaterialshader_wrapper.cpp
+${QtQuick_GEN_DIR}/qsgmaterialshader_graphicspipelinestate_wrapper.cpp
${QtQuick_GEN_DIR}/qsgmaterialtype_wrapper.cpp
+${QtQuick_GEN_DIR}/qsgninepatchnode_wrapper.cpp
${QtQuick_GEN_DIR}/qsgnode_wrapper.cpp
+${QtQuick_GEN_DIR}/qsgnodevisitor_wrapper.cpp
${QtQuick_GEN_DIR}/qsgopacitynode_wrapper.cpp
${QtQuick_GEN_DIR}/qsgopaquetexturematerial_wrapper.cpp
#${QtQuick_GEN_DIR}/qsgsimplematerial_wrapper.cpp
#${QtQuick_GEN_DIR}/qsgsimplematerialshader_wrapper.cpp
${QtQuick_GEN_DIR}/qsgrectanglenode_wrapper.cpp
${QtQuick_GEN_DIR}/qsgrendernode_wrapper.cpp
+${QtQuick_GEN_DIR}/qsgrootnode_wrapper.cpp
${QtQuick_GEN_DIR}/qsgsimplerectnode_wrapper.cpp
${QtQuick_GEN_DIR}/qsgsimpletexturenode_wrapper.cpp
${QtQuick_GEN_DIR}/qsgrendererinterface_wrapper.cpp
+${QtQuick_GEN_DIR}/qsgtextnode_wrapper.cpp
${QtQuick_GEN_DIR}/qsgtexture_wrapper.cpp
${QtQuick_GEN_DIR}/qsgtexturematerial_wrapper.cpp
${QtQuick_GEN_DIR}/qsgtextureprovider_wrapper.cpp
${Qt${QT_MAJOR_VERSION}Core_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Core_PRIVATE_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Gui_INCLUDE_DIRS}
- ${Qt${QT_MAJOR_VERSION}OpenGL_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Network_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Qml_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Qml_PRIVATE_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Quick_INCLUDE_DIRS}
${libpyside_SOURCE_DIR}
${QtGui_GEN_DIR}
- ${QtOpenGL_GEN_DIR}
${QtCore_GEN_DIR}
${QtNetwork_GEN_DIR}
${QtQml_GEN_DIR}
${QtQuick_GEN_DIR})
set(QtQuick_libraries pyside6 pyside6qml
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Gui_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}OpenGL_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Network_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Qml_LIBRARIES}
${Qt${QT_MAJOR_VERSION}Quick_LIBRARIES})
-set(QtQuick_deps QtGui QtOpenGL QtNetwork QtQml)
+set(QtQuick_deps QtGui QtNetwork QtQml)
+
+check_qt_opengl("Quick" QtQuick_include_dirs QtQuick_deps
+ QtQuick_DROPPED_ENTRIES)
+
+if (Qt${QT_MAJOR_VERSION}OpenGL_FOUND)
+ list(APPEND QtQuick_SRC
+ ${QtQuick_GEN_DIR}/qquickframebufferobject_wrapper.cpp
+ ${QtQuick_GEN_DIR}/qquickframebufferobject_renderer_wrapper.cpp)
+else()
+ list(APPEND QtQuick_DROPPED_ENTRIES QQuickFramebufferObject)
+endif()
create_pyside_module(NAME QtQuick
INCLUDE_DIRS QtQuick_include_dirs
DEPS QtQuick_deps
TYPESYSTEM_PATH QtQuick_SOURCE_DIR
SOURCES QtQuick_SRC
- STATIC_SOURCES QtQuick_registerType)
+ STATIC_SOURCES QtQuick_registerType
+ DROPPED_ENTRIES QtQuick_DROPPED_ENTRIES)
#include <shiboken.h>
#include <QtQuick/QQuickPaintedItem>
-#include <QtQuick/QQuickFramebufferObject>
+
+#if QT_CONFIG(opengl) || QT_CONFIG(opengles2) || QT_CONFIG(opengles3)
+# include <QtQuick/QQuickFramebufferObject>
+#endif
bool pyTypeObjectInheritsFromClass(PyTypeObject *pyObjType, const char *classPtrName)
{
template <class WrappedClass>
bool registerTypeIfInheritsFromClass(const char *classPtrName,
PyTypeObject *typeToRegister,
- QQmlPrivate::RegisterType *type)
+ QQmlPrivate::RegisterTypeAndRevisions *type)
{
if (!pyTypeObjectInheritsFromClass(typeToRegister, classPtrName))
return false;
return true;
}
-bool quickRegisterType(PyObject *pyObj, QQmlPrivate::RegisterType *type)
+bool quickRegisterType(PyObject *pyObj, QQmlPrivate::RegisterTypeAndRevisions *type)
{
using namespace Shiboken;
return registerTypeIfInheritsFromClass<QQuickPaintedItem>("QQuickPaintedItem*",
pyObjType, type)
+#if QT_CONFIG(opengl) || QT_CONFIG(opengles2) || QT_CONFIG(opengles3)
|| registerTypeIfInheritsFromClass<QQuickFramebufferObject>("QQuickFramebufferObject*",
pyObjType, type)
+#endif
|| registerTypeIfInheritsFromClass<QQuickItem>("QQuickItem*",
pyObjType, type);
}
Q_UNUSED(module);
// We need to manually register a pointer version of these types in order for them to be used as property types.
qRegisterMetaType<QQuickPaintedItem*>("QQuickPaintedItem*");
+#if QT_CONFIG(opengl) || QT_CONFIG(opengles2) || QT_CONFIG(opengles3)
qRegisterMetaType<QQuickFramebufferObject*>("QQuickFramebufferObject*");
+#endif
qRegisterMetaType<QQuickItem*>("QQuickItem*");
Qml::setQuickRegisterItemFunction(quickRegisterType);
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtQuick">
+<typesystem package="PySide6.QtQuick"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<load-typesystem name="QtNetwork/typesystem_network.xml" generate="no"/>
+ <load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/>
<!-- QQuickFramebufferObject::Renderer needs QOpenGLFramebufferObject -->
+ <?if !no_QtOpenGL?>
<load-typesystem name="QtOpenGL/typesystem_opengl.xml" generate="no"/>
+ <?endif?>
<load-typesystem name="QtQml/typesystem_qml.xml" generate="no"/>
<smart-pointer-type name="QSharedPointer" type="shared" getter="data"
<!-- TODO: Find a way to wrap `union ItemChangeData {}` -->
</object-type>
+ <object-type name="QQuickRhiItemRenderer" since="6.7"/>
+ <object-type name="QQuickRhiItem" since="6.7">
+ <enum-type name="TextureFormat"/>
+ </object-type>
+
<object-type name="QQuickItemGrabResult"/>
<object-type name="QQuickPaintedItem">
<modify-function signature="^fromVulkanImage\(.*$" remove="all"/>
</value-type>
- <object-type name="QQuickTextDocument"/>
+ <object-type name="QQuickTextDocument">
+ <enum-type name="Status" since="6.7"/>
+ </object-type>
<object-type name="QQuickView">
<enum-type name="ResizeMode"/>
<enum-type name="RenderStage"/>
<enum-type name="SceneGraphError"/>
<enum-type name="TextRenderType"/>
+ <value-type name="GraphicsStateInfo"/>
</object-type>
<object-type name="QSGBasicGeometryNode">
</modify-function>
</object-type>
+ <object-type name="QSGImageNode">
+ <enum-type name="TextureCoordinatesTransformFlag" flags="TextureCoordinatesTransformMode"/>
+ </object-type>
+
<object-type name="QSGMaterial">
<enum-type name="Flag" flags="Flags"/>
</object-type>
<value-type name="RenderState">
<enum-type name="DirtyState" flags="DirtyStates"/>
</value-type>
+ <value-type name="GraphicsPipelineState">
+ <enum-type name="BlendFactor"/>
+ <enum-type name="ColorMaskComponent" flags="ColorMask"/>
+ <enum-type name="CullMode"/>
+ <enum-type name="PolygonMode"/>
+ </value-type>
<modify-function signature="updateSampledImage(QSGMaterialShader::RenderState&,int,QSGTexture**,QSGMaterial*,QSGMaterial*)" remove="all"/>
<!-- Private QRhi class -->
<modify-function signature="setShader(QSGMaterialShader::Stage,QShader)" remove="all"/>
</object-type>
<object-type name="QSGMaterialType"/>
+ <object-type name="QSGNinePatchNode"/>
<object-type name="QSGNode">
<enum-type name="DirtyStateBit" flags="DirtyState"/>
<enum-type name="Flag" flags="Flags"/>
<enum-type name="NodeType"/>
</object-type>
+ <object-type name="QSGNodeVisitor"/>
+
<object-type name="QSGOpacityNode"/>
<object-type name="QSGOpaqueTextureMaterial"/>
<object-type name="QSGSimpleRectNode"/>
<object-type name="QSGSimpleTextureNode">
<enum-type name="TextureCoordinatesTransformFlag" flags="TextureCoordinatesTransformMode"/>
</object-type>
+ <object-type name="QSGTextNode" since="6.7">
+ <enum-type name="TextStyle"/>
+ <enum-type name="RenderType"/>
+ </object-type>
<object-type name="QSGRectangleNode"/>
<object-type name="QSGRendererInterface">
<enum-type name="GraphicsApi"/>
<enum-type name="RenderingFlag" flags="RenderingFlags"/>
<object-type name="RenderState"/>
</object-type>
+ <object-type name="QSGRootNode"/>
<object-type name="QSGTexture">
<enum-type name="AnisotropyLevel"/>
<enum-type name="Filtering"/>
project(QtQuick3D)
+set (QtQuick3D_DROPPED_ENTRIES)
+
set(QtQuick3D_SRC
${QtQuick3D_GEN_DIR}/qquick3d_wrapper.cpp
${QtQuick3D_GEN_DIR}/qquick3dobject_wrapper.cpp
${QtQuick3D_GEN_DIR}/qquick3dgeometry_targetattribute_wrapper.cpp
${QtQuick3D_GEN_DIR}/qquick3dinstancing_wrapper.cpp
${QtQuick3D_GEN_DIR}/qquick3dinstancing_instancetableentry_wrapper.cpp
+${QtQuick3D_GEN_DIR}/qquick3drenderextension_wrapper.cpp
${QtQuick3D_GEN_DIR}/qquick3dtexturedata_wrapper.cpp
# module is always needed
${QtQuick3D_GEN_DIR}/qtquick3d_module_wrapper.cpp
${Qt${QT_MAJOR_VERSION}Core_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Core_PRIVATE_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Gui_INCLUDE_DIRS}
- ${Qt${QT_MAJOR_VERSION}OpenGL_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Network_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Qml_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Qml_PRIVATE_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Quick3D_INCLUDE_DIRS}
${libpyside_SOURCE_DIR}
${QtGui_GEN_DIR}
- ${QtOpenGL_GEN_DIR}
${QtCore_GEN_DIR}
${QtNetwork_GEN_DIR}
${QtQml_GEN_DIR}
${QtQuick3D_GEN_DIR})
set(QtQuick3D_libraries pyside6
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Gui_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}OpenGL_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Network_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Qml_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Quick_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Quick3D_LIBRARIES})
+ ${Qt${QT_MAJOR_VERSION}Quick3D_LIBRARIES})
+
+set(QtQuick3D_deps QtGui QtNetwork QtQml QtQuick)
-set(QtQuick3D_deps QtGui QtOpenGL QtNetwork QtQml QtQuick)
+check_qt_opengl("Quick3D" QtQuick3D_include_dirs QtQuick3D_deps
+ QtQuick3D_DROPPED_ENTRIES)
create_pyside_module(NAME QtQuick3D
INCLUDE_DIRS QtQuick3D_include_dirs
LIBRARIES QtQuick3D_libraries
DEPS QtQuick3D_deps
TYPESYSTEM_PATH QtQuick3D_SOURCE_DIR
- SOURCES QtQuick3D_SRC)
+ SOURCES QtQuick3D_SRC
+ DROPPED_ENTRIES QtQuick3D_DROPPED_ENTRIES)
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtQuick3D">
+<typesystem package="PySide6.QtQuick3D"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtQuick/typesystem_quick.xml" generate="no"/>
<object-type name="QQuick3D"/>
<object-type name="QQuick3DTextureData">
<enum-type name="Format"/>
</object-type>
+ <object-type name="QQuick3DRenderExtension" since="6.7"/>
</typesystem>
project(QtQuickControls2)
+set (QtQuickControls2_DROPPED_ENTRIES)
+
set(QtQuickControls2_SRC
${QtQuickControls2_GEN_DIR}/qquickstyle_wrapper.cpp
+${QtQuickControls2_GEN_DIR}/qquickattachedpropertypropagator_wrapper.cpp
# module is always needed
${QtQuickControls2_GEN_DIR}/qtquickcontrols2_module_wrapper.cpp
)
${QtQml_SOURCE_DIR}
${Qt${QT_MAJOR_VERSION}Core_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Gui_INCLUDE_DIRS}
- ${Qt${QT_MAJOR_VERSION}OpenGL_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Network_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Qml_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Quick_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}QuickControls2_INCLUDE_DIRS}
${libpyside_SOURCE_DIR}
${QtGui_GEN_DIR}
- ${QtOpenGL_GEN_DIR}
${QtCore_GEN_DIR}
${QtNetwork_GEN_DIR}
${QtQml_GEN_DIR}
${QtQuickControls2_GEN_DIR})
set(QtQuickControls2_libraries pyside6
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Gui_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}OpenGL_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Network_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Qml_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Quick_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}QuickControls2_LIBRARIES})
+ ${Qt${QT_MAJOR_VERSION}QuickControls2_LIBRARIES})
+
+set(QtQuickControls2_deps QtGui QtNetwork QtQml QtQuick)
-set(QtQuickControls2_deps QtGui QtOpenGL QtNetwork QtQml QtQuick)
+check_qt_opengl("QuickControls2" QtQuickControls2_include_dirs QtQuickControls2_deps
+ QtQuickControls2_DROPPED_ENTRIES)
create_pyside_module(NAME QtQuickControls2
INCLUDE_DIRS QtQuickControls2_include_dirs
LIBRARIES QtQuickControls2_libraries
DEPS QtQuickControls2_deps
TYPESYSTEM_PATH QtQuickControls2_SOURCE_DIR
- SOURCES QtQuickControls2_SRC)
+ SOURCES QtQuickControls2_SRC
+ DROPPED_ENTRIES QtQuickControls2_DROPPED_ENTRIES)
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtQuickControls2">
+<typesystem package="PySide6.QtQuickControls2"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtQuick/typesystem_quick.xml" generate="no"/>
<object-type name="QQuickStyle"/>
+ <object-type name="QQuickAttachedPropertyPropagator"/>
</typesystem>
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+project(QtQuickTest)
+
+set (QtQuickTest_DROPPED_ENTRIES)
+
+set(QtQuickTest_SRC
+# module is always needed
+${QtQuickTest_GEN_DIR}/qtquicktest_module_wrapper.cpp
+)
+
+set(QtQuickTest_include_dirs ${QtQuickTest_SOURCE_DIR}
+ ${QtQml_SOURCE_DIR}
+ ${Qt${QT_MAJOR_VERSION}Core_INCLUDE_DIRS}
+ ${Qt${QT_MAJOR_VERSION}Gui_INCLUDE_DIRS}
+ ${Qt${QT_MAJOR_VERSION}OpenGL_INCLUDE_DIRS}
+ ${Qt${QT_MAJOR_VERSION}Network_INCLUDE_DIRS}
+ ${Qt${QT_MAJOR_VERSION}Qml_INCLUDE_DIRS}
+ ${Qt${QT_MAJOR_VERSION}Quick_INCLUDE_DIRS}
+ ${libpyside_SOURCE_DIR}
+ ${QtGui_GEN_DIR}
+ ${QtOpenGL_GEN_DIR}
+ ${QtCore_GEN_DIR}
+ ${QtNetwork_GEN_DIR}
+ ${QtQml_GEN_DIR}
+ ${QtQuick_GEN_DIR}
+ ${QtQuickTest_GEN_DIR})
+
+set(QtQuickTest_libraries pyside6
+ ${Qt${QT_MAJOR_VERSION}QuickTest_LIBRARIES})
+
+set(QtQuickTest_deps QtGui QtNetwork QtQml QtQuick)
+
+check_qt_opengl("QuickTest" QtQuickTest_include_dirs QtQuickTest_deps
+ QtQuickTest_DROPPED_ENTRIES)
+
+create_pyside_module(NAME QtQuickTest
+ INCLUDE_DIRS QtQuickTest_include_dirs
+ LIBRARIES QtQuickTest_libraries
+ DEPS QtQuickTest_deps
+ TYPESYSTEM_PATH QtQuickTest_SOURCE_DIR
+ SOURCES QtQuickTest_SRC
+ DROPPED_ENTRIES QtQuickTest_DROPPED_ENTRIES)
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+-->
+<typesystem package="PySide6.QtQuickTest"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
+ <load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
+
+ <extra-includes>
+ <include file-name="QtQuickTest/quicktest.h" location="global"/>
+ <include file-name="QtCore/QDir" location="global"/>
+ <include file-name="pysideqobject.h" location="global"/>
+ <include file-name="vector" location="global"/>
+ </extra-includes>
+ <inject-code class="native" position="beginning"
+ file="../glue/qtquicktest.cpp" snippet="call-quick-test-main"/>
+
+ <add-function signature="QUICK_TEST_MAIN(QString@name@,QStringList@argv@={},QString@dir@={})"
+ return-type="int">
+ <inject-code file="../glue/qtquicktest.cpp" snippet="quick-test-main"/>
+ <inject-documentation format="target" mode="append"
+ file="../doc/qtquicktest.rst"
+ snippet="quick_test_main_documentation"/>
+ </add-function>
+ <add-function signature="QUICK_TEST_MAIN_WITH_SETUP(QString@name@,PyTypeObject*@setup@,QStringList@argv@={},QString@dir@={})"
+ return-type="int">
+ <inject-code file="../glue/qtquicktest.cpp" snippet="quick-test-main_with_setup"/>
+ <inject-documentation format="target" mode="append"
+ file="../doc/qtquicktest.rst"
+ snippet="quick_test_main_with_setup_documentation"/>
+ </add-function>
+</typesystem>
project(QtQuickWidgets)
+set (QtQuickWidgets_DROPPED_ENTRIES)
+
set(QtQuickWidgets_SRC
${QtQuickWidgets_GEN_DIR}/qquickwidget_wrapper.cpp
# module is always needed
${QtQuickWidgets_GEN_DIR})
set(QtQuickWidgets_libraries pyside6
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Gui_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}OpenGL_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Network_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Widgets_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Quick_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Qml_LIBRARIES}
${Qt${QT_MAJOR_VERSION}QuickWidgets_LIBRARIES})
-set(QtQuickWidgets_deps QtGui QtOpenGL QtQml QtQuick QtWidgets QtNetwork)
+set(QtQuickWidgets_deps QtGui QtQml QtQuick QtWidgets QtNetwork)
+
+check_qt_opengl("QuickWidgets" QtQuickWidgets_include_dirs QtQuickWidgets_deps
+ QtQuickWidgets_DROPPED_ENTRIES)
create_pyside_module(NAME QtQuickWidgets
INCLUDE_DIRS QtQuickWidgets_include_dirs
LIBRARIES QtQuickWidgets_libraries
DEPS QtQuickWidgets_deps
TYPESYSTEM_PATH QtQuickWidgets_SOURCE_DIR
- SOURCES QtQuickWidgets_SRC)
+ SOURCES QtQuickWidgets_SRC
+ DROPPED_ENTRIES QtQuickWidgets_DROPPED_ENTRIES)
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtQuickWidgets">
+<typesystem package="PySide6.QtQuickWidgets"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/>
<load-typesystem name="QtQuick/typesystem_quick.xml" generate="no"/>
set(QtRemoteObjects_SRC
${QtRemoteObjects_GEN_DIR}/qabstractitemmodelreplica_wrapper.cpp
+${QtRemoteObjects_GEN_DIR}/qconnectionabstractserver_wrapper.cpp
${QtRemoteObjects_GEN_DIR}/qremoteobjectabstractpersistedstore_wrapper.cpp
${QtRemoteObjects_GEN_DIR}/qremoteobjectdynamicreplica_wrapper.cpp
${QtRemoteObjects_GEN_DIR}/qremoteobjecthost_wrapper.cpp
${QtRemoteObjects_GEN_DIR}/qremoteobjectregistry_wrapper.cpp
${QtRemoteObjects_GEN_DIR}/qremoteobjectregistryhost_wrapper.cpp
${QtRemoteObjects_GEN_DIR}/qremoteobjectreplica_wrapper.cpp
-# ${QtRemoteObjects_GEN_DIR}/qtremoteobjects_wrapper.cpp
+${QtRemoteObjects_GEN_DIR}/qtremoteobjects_wrapper.cpp
${QtRemoteObjects_GEN_DIR}/qremoteobjectsettingsstore_wrapper.cpp
${QtRemoteObjects_GEN_DIR}/qremoteobjectsourcelocationinfo_wrapper.cpp
+${QtRemoteObjects_GEN_DIR}/qtroclientfactory_wrapper.cpp
+${QtRemoteObjects_GEN_DIR}/qtroclientiodevice_wrapper.cpp
+${QtRemoteObjects_GEN_DIR}/qtroiodevicebase_wrapper.cpp
+${QtRemoteObjects_GEN_DIR}/qtroserverfactory_wrapper.cpp
+${QtRemoteObjects_GEN_DIR}/qtroserveriodevice_wrapper.cpp
# module is always needed
${QtRemoteObjects_GEN_DIR}/qtremoteobjects_module_wrapper.cpp
${SHIBOKEN_INCLUDE_DIR}
${libpyside_SOURCE_DIR}
${SHIBOKEN_PYTHON_INCLUDE_DIR}
- ${QtCore_GEN_DIR})
+ ${QtCore_GEN_DIR}
+ ${QtNetwork_GEN_DIR})
set(QtRemoteObjects_libraries pyside6
- ${SHIBOKEN_PYTHON_LIBRARIES}
- ${SHIBOKEN_LIBRARY}
${Qt${QT_MAJOR_VERSION}RemoteObjects_LIBRARIES})
set(QtRemoteObjects_deps QtCore QtNetwork)
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtRemoteObjects">
+<typesystem package="PySide6.QtRemoteObjects"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="templates/core_common.xml" generate="no"/>
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
+ <load-typesystem name="QtNetwork/typesystem_network.xml" generate="no"/>
<rejection class="QRemoteObjectStringLiterals"/>
<rejection class="*" function-name="getTypeNameAndMetaobjectFromClassInfo"/>
-<!-- Exclude namespace due to Q_NAMESPACE link errors on Windows (QTBUG-68014)
<rejection class="QtRemoteObjects" field-name="staticMetaObject"/>
<namespace-type name="QtRemoteObjects">
<enum-type name="InitialAction"/>
<enum-type name="QRemoteObjectPacketTypeEnum"/>
</namespace-type>
--->
<object-type name="QAbstractItemModelReplica"/>
+ <object-type name="QConnectionAbstractServer"/>
<object-type name="QRemoteObjectAbstractPersistedStore"/>
<object-type name="QRemoteObjectDynamicReplica"/>
<object-type name="QRemoteObjectHost"/>
</object-type>
<object-type name="QRemoteObjectSettingsStore"/>
<value-type name="QRemoteObjectSourceLocationInfo"/>
+ <object-type name="QtROClientFactory"/>
+ <object-type name="QtROClientIoDevice"/>
+ <object-type name="QtROIoDeviceBase"/>
+ <object-type name="QtROServerFactory"/>
+ <object-type name="QtROServerIoDevice"/>
<suppress-warning text="^.*Typedef used on signal QRemoteObject.*$"/>
<suppress-warning text="^QRemoteObjectPendingCallWatcher inherits from a non polymorphic type.*$"/>
<suppress-warning text="^Enum 'QRemoteObjectReplica::ConstructorType'.*does not have a type entry.*$"/>
<suppress-warning text="Stripping argument #1 of void QRemoteObjectReplica::QRemoteObjectReplica(QRemoteObjectReplica::ConstructorType) due to unmatched type "QRemoteObjectReplica::ConstructorType" with default expression "DefaultConstructor"."/>
- <suppress-warning text="skipping field 'QRemoteObjectReplica::d_impl' with unmatched type 'QSharedPointer'"/>
+ <suppress-warning text="skipping protected field 'QRemoteObjectReplica::d_impl' with unmatched type 'QSharedPointer'"/>
<!-- QtNetwork is pulled in via QtRemoteObjectsDepends. -->
<suppress-warning text="^Scoped enum 'Q(Ocsp)|(Dtls).*' does not have a type entry.*$"/>
// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtScxml">
+<typesystem package="PySide6.QtScxml"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<object-type name="QScxmlCompiler">
<object-type name="Loader"/>
// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtSensors">
+<typesystem package="PySide6.QtSensors"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<!-- overrides QObject::metaObject() by private method
<object-type name="QSensorGesture"/>
set(QtSerialBus_SRC
${QtSerialBus_GEN_DIR}/qcanbus_wrapper.cpp
- ${QtSerialBus_GEN_DIR}/qcanbusdevice_wrapper.cpp
${QtSerialBus_GEN_DIR}/qcanbusdevice_filter_wrapper.cpp
+ ${QtSerialBus_GEN_DIR}/qcanbusdevice_wrapper.cpp
${QtSerialBus_GEN_DIR}/qcanbusdeviceinfo_wrapper.cpp
${QtSerialBus_GEN_DIR}/qcanbusfactory_wrapper.cpp
- ${QtSerialBus_GEN_DIR}/qcanbusframe_wrapper.cpp
${QtSerialBus_GEN_DIR}/qcanbusframe_timestamp_wrapper.cpp
+ ${QtSerialBus_GEN_DIR}/qcanbusframe_wrapper.cpp
+ ${QtSerialBus_GEN_DIR}/qcandbcfileparser_wrapper.cpp
+ ${QtSerialBus_GEN_DIR}/qcanframeprocessor_parseresult_wrapper.cpp
+ ${QtSerialBus_GEN_DIR}/qcanframeprocessor_wrapper.cpp
+ ${QtSerialBus_GEN_DIR}/qcanmessagedescription_wrapper.cpp
+ ${QtSerialBus_GEN_DIR}/qcansignaldescription_multiplexvaluerange_wrapper.cpp
+ ${QtSerialBus_GEN_DIR}/qcansignaldescription_wrapper.cpp
+ ${QtSerialBus_GEN_DIR}/qcanuniqueiddescription_wrapper.cpp
${QtSerialBus_GEN_DIR}/qmodbusclient_wrapper.cpp
${QtSerialBus_GEN_DIR}/qmodbusdataunit_wrapper.cpp
${QtSerialBus_GEN_DIR}/qmodbusdevice_wrapper.cpp
${QtSerialBus_GEN_DIR}/qmodbusdeviceidentification_wrapper.cpp
+ ${QtSerialBus_GEN_DIR}/qmodbusexceptionresponse_wrapper.cpp
${QtSerialBus_GEN_DIR}/qmodbuspdu_wrapper.cpp
${QtSerialBus_GEN_DIR}/qmodbusreply_wrapper.cpp
${QtSerialBus_GEN_DIR}/qmodbusrequest_wrapper.cpp
+ ${QtSerialBus_GEN_DIR}/qmodbusresponse_wrapper.cpp
${QtSerialBus_GEN_DIR}/qmodbusrtuserialclient_wrapper.cpp
${QtSerialBus_GEN_DIR}/qmodbusrtuserialserver_wrapper.cpp
${QtSerialBus_GEN_DIR}/qmodbusserver_wrapper.cpp
${QtSerialBus_GEN_DIR}/qmodbustcpclient_wrapper.cpp
${QtSerialBus_GEN_DIR}/qmodbustcpconnectionobserver_wrapper.cpp
${QtSerialBus_GEN_DIR}/qmodbustcpserver_wrapper.cpp
+ ${QtSerialBus_GEN_DIR}/qtcanbus_wrapper.cpp
# module is always needed
${QtSerialBus_GEN_DIR}/qtserialbus_module_wrapper.cpp
)
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtSerialBus">
+<typesystem package="PySide6.QtSerialBus"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<load-typesystem name="QtNetwork/typesystem_network.xml" generate="no"/>
<load-typesystem name="QtSerialPort/typesystem_serialport.xml" generate="no"/>
+ <namespace-type name="QtCanBus">
+ <enum-type name="DataSource"/>
+ <enum-type name="DataFormat"/>
+ <enum-type name="MultiplexState"/>
+ <enum-type name="UniqueId"/>
+ </namespace-type>
+
<object-type name="QCanBus">
<!-- Remove errorMessage argument, return tuple instead. -->
<modify-function signature="availableDevices(QString,QString*)const">
<enum-type name="FrameError" flags="FrameErrors"/>
<value-type name="TimeStamp"/>
</value-type>
+ <object-type name="QCanDbcFileParser">
+ <enum-type name="Error"/>
+ </object-type>
+ <object-type name="QCanFrameProcessor">
+ <enum-type name="Error"/>
+ <value-type name="ParseResult"/>
+ </object-type>
+ <value-type name="QCanMessageDescription"/>
+ <value-type name="QCanSignalDescription">
+ <value-type name="MultiplexValueRange"/>
+ </value-type>
+ <value-type name="QCanUniqueIdDescription"/>
<object-type name="QModbusClient"/>
<value-type name="QModbusDataUnit">
<enum-type name="RegisterType"/>
<enum-type name="FunctionCode"/>
<modify-field name="ExceptionByte" remove="true"/> <!-- Link error -->
</object-type>
+ <object-type name="QModbusExceptionResponse"/>
+ <object-type name="QModbusResponse"/>
<object-type name="QModbusReply">
<enum-type name="ReplyType"/>
</object-type>
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtSerialPort">
+<typesystem package="PySide6.QtSerialPort"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<object-type name="QSerialPort">
<enum-type name="BaudRate" python-type="IntEnum"/>
${QtMultimedia_GEN_DIR})
set(QtSpatialAudio_libraries pyside6
- ${Qt${QT_MAJOR_VERSION}SpatialAudio_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Gui_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Network_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Multimedia_LIBRARIES}
- )
+ ${Qt${QT_MAJOR_VERSION}SpatialAudio_LIBRARIES})
+
set(QtSpatialAudio_deps QtCore QtGui QtNetwork QtMultimedia)
create_pyside_module(NAME QtSpatialAudio
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtSpatialAudio">
+<typesystem package="PySide6.QtSpatialAudio"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/>
<load-typesystem name="QtNetwork/typesystem_network.xml" generate="no"/>
${libpyside_SOURCE_DIR}
${QtCore_GEN_DIR}
${QtGui_GEN_DIR}
- ${QtWidgets_GEN_DIR}
- )
+ ${QtWidgets_GEN_DIR})
+
+# Link to QtWidgets to enable QSqlRelationalDelegate
set(QtSql_libraries pyside6
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Gui_LIBRARIES}
${Qt${QT_MAJOR_VERSION}Widgets_LIBRARIES}
${Qt${QT_MAJOR_VERSION}Sql_LIBRARIES})
+
set(QtSql_deps QtWidgets)
create_pyside_module(NAME QtSql
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtSql">
+<typesystem package="PySide6.QtSql"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtWidgets/typesystem_widgets.xml" generate="no"/>
<rejection class="QSqlDriverCreator"/>
${Qt${QT_MAJOR_VERSION}Gui_INCLUDE_DIRS}
${libpyside_SOURCE_DIR}
${QtCore_GEN_DIR}
- ${QtGui_GEN_DIR}
- )
+ ${QtGui_GEN_DIR})
+
set(QtStateMachine_libraries pyside6
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Gui_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}StateMachine_LIBRARIES}
- )
+ ${Qt${QT_MAJOR_VERSION}StateMachine_LIBRARIES})
+
set(QtStateMachine_deps QtGui)
create_pyside_module(NAME QtStateMachine
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtStateMachine">
+<typesystem package="PySide6.QtStateMachine"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/>
<object-type name="QAbstractState">
set(QtSvg_SRC
${QtSvg_GEN_DIR}/qsvggenerator_wrapper.cpp
${QtSvg_GEN_DIR}/qsvgrenderer_wrapper.cpp
+${QtSvg_GEN_DIR}/qtsvg_wrapper.cpp
# module is always needed
${QtSvg_GEN_DIR}/qtsvg_module_wrapper.cpp
)
${Qt${QT_MAJOR_VERSION}Svg_INCLUDE_DIRS}
${libpyside_SOURCE_DIR}
${QtCore_GEN_DIR}
- ${QtGui_GEN_DIR}
- )
+ ${QtGui_GEN_DIR})
+
set(QtSvg_libraries pyside6
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Gui_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Svg_LIBRARIES}
- )
+ ${Qt${QT_MAJOR_VERSION}Svg_LIBRARIES})
+
+
set(QtSvg_deps QtGui)
create_pyside_module(NAME QtSvg
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtSvg">
+<typesystem package="PySide6.QtSvg"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/>
<object-type name="QSvgRenderer"/>
+ <namespace-type name="QtSvg" since="6.7">
+ <enum-type name="Option" flags="Options"/>
+ </namespace-type>
<object-type name="QSvgGenerator">
<enum-type name="SvgVersion" since="6.5"/>
${QtCore_GEN_DIR}
${QtGui_GEN_DIR}
${QtWidgets_GEN_DIR}
- ${QtSvg_GEN_DIR}
- )
+ ${QtSvg_GEN_DIR})
set(QtSvgWidgets_libraries pyside6
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Gui_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Widgets_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Svg_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}SvgWidgets_LIBRARIES}
- )
+ ${Qt${QT_MAJOR_VERSION}SvgWidgets_LIBRARIES})
set(QtSvgWidgets_deps QtSvg QtWidgets)
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtSvgWidgets">
+<typesystem package="PySide6.QtSvgWidgets"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtSvg/typesystem_svg.xml" generate="no"/>
<load-typesystem name="QtWidgets/typesystem_widgets.xml" generate="no"/>
${libpyside_SOURCE_DIR}
${QtCore_GEN_DIR}
${QtGui_GEN_DIR}
- ${QtWidgets_GEN_DIR}
- )
+ ${QtWidgets_GEN_DIR})
+
+# Link to QtGui/QtWidgets to enable gui/widget-specific inline functions
set(QtTest_libraries pyside6
${Qt${QT_MAJOR_VERSION}Test_LIBRARIES}
${Qt${QT_MAJOR_VERSION}Widgets_LIBRARIES}
${Qt${QT_MAJOR_VERSION}Gui_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- )
+ ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES})
+
set(QtTest_deps QtWidgets)
create_pyside_module(NAME QtTest
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtTest">
+<typesystem package="PySide6.QtTest"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/>
<load-typesystem name="QtWidgets/typesystem_widgets.xml" generate="no"/>
${QtCore_GEN_DIR})
set(QtTextToSpeech_libraries pyside6
- ${Qt${QT_MAJOR_VERSION}Multimedia_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}TextToSpeech_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES})
+ ${Qt${QT_MAJOR_VERSION}TextToSpeech_LIBRARIES})
set(QtTextToSpeech_deps QtCore QtMultimedia)
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtTextToSpeech">
+<typesystem package="PySide6.QtTextToSpeech"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<object-type name="QTextToSpeech">
${plugins_SOURCE_DIR}
${QtCore_GEN_DIR}
${QtGui_GEN_DIR}
- ${QtWidgets_GEN_DIR}
- )
+ ${QtWidgets_GEN_DIR})
+
set(QtUiTools_libraries pyside6
uiplugin
- ${Qt${QT_MAJOR_VERSION}UiTools_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Gui_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Widgets_LIBRARIES}
- )
+ ${Qt${QT_MAJOR_VERSION}UiTools_LIBRARIES})
+
set(QtUiTools_deps QtWidgets)
configure_file("${QtUiTools_SOURCE_DIR}/QtUiTools_global.pre.h.in"
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtUiTools">
+<typesystem package="PySide6.QtUiTools"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtWidgets/typesystem_widgets.xml" generate="no"/>
<object-type name="QUiLoader">
<inject-code class="native" position="beginning" file="../glue/qtuitools.cpp" snippet="uitools-loadui"/>
<inject-code file="../glue/qtuitools.cpp" snippet="quiloader"/>
<add-function signature="registerCustomWidget(PyObject*@customWidgetType@)" return-type="void">
- <inject-documentation format="target" mode="append">
- Registers a Python created custom widget to QUiLoader, so it can be recognized when
- loading a `.ui` file. The custom widget type is passed via the ``customWidgetType`` argument.
- This is needed when you want to override a virtual method of some widget in the interface,
- since duck punching will not work with widgets created by QUiLoader based on the contents
- of the `.ui` file.
-
- (Remember that `duck punching virtual methods is an invitation for your own demise!
- <https://doc.qt.io/qtforpython/shiboken6/wordsofadvice.html#duck-punching-and-virtual-methods>`_)
-
- Let's see an obvious example. If you want to create a new widget it's probable you'll end up
- overriding :class:`~PySide6.QtGui.QWidget`'s :meth:`~PySide6.QtGui.QWidget.paintEvent` method.
-
- .. code-block:: python
-
- class Circle(QWidget):
- def paintEvent(self, event):
- with QPainter(self) as painter:
- painter.setPen(self.pen)
- painter.setBrush(QBrush(self.color))
- painter.drawEllipse(event.rect().center(), 20, 20)
-
- # ...
-
- loader = QUiLoader()
- loader.registerCustomWidget(Circle)
- circle = loader.load('circle.ui')
- circle.show()
-
- # ...
- </inject-documentation>
- <inject-code class="target" position="beginning" file="../glue/qtuitools.cpp" snippet="quiloader-registercustomwidget"/>
+ <inject-documentation format="target" mode="append" file="../doc/qtuitools.rst"
+ snippet="quiloader-registercustomwidget"/>
+ <inject-code class="target" position="beginning" file="../glue/qtuitools.cpp"
+ snippet="quiloader-registercustomwidget"/>
</add-function>
<modify-function signature="createAction(QObject*,const QString&)">
<modify-argument index="return">
-->
<add-function signature="loadUiType(const QString& @uifile@)" return-type="PyObject*">
<inject-code file="../glue/qtuitools.cpp" snippet="loaduitype"/>
+ <inject-documentation format="target" mode="append" file="../doc/qtuitools.rst"
+ snippet="loaduitype"/>
</add-function>
${Qt${QT_MAJOR_VERSION}Core_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}WebChannel_INCLUDE_DIRS}
${libpyside_SOURCE_DIR}
- ${QtCore_GEN_DIR}
- )
-set(QtWebChannel_libraries pyside6
- ${Qt${QT_MAJOR_VERSION}WebChannel_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- )
+ ${QtCore_GEN_DIR})
+
+set(QtWebChannel_libraries pyside6
+ ${Qt${QT_MAJOR_VERSION}WebChannel_LIBRARIES})
+
set(QtWebChannel_deps QtCore)
create_pyside_module(NAME QtWebChannel
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtWebChannel">
+<typesystem package="PySide6.QtWebChannel"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<object-type name="QWebChannel"/>
set(QtWebEngineCore_SRC
${QtWebEngineCore_GEN_DIR}/qwebenginecertificateerror_wrapper.cpp
+${QtWebEngineCore_GEN_DIR}/qwebengineclientcertificateselection_wrapper.cpp
+${QtWebEngineCore_GEN_DIR}/qwebengineclientcertificatestore_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebenginecontextmenurequest_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebenginecookiestore_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebenginecookiestore_filterrequest_wrapper.cpp
+# FIXME ${QtWebEngineCore_GEN_DIR}/qwebenginedesktopmediarequest_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebenginedownloadrequest_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebenginefilesystemaccessrequest_wrapper.cpp
+${QtWebEngineCore_GEN_DIR}/qwebenginefindtextresult_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebenginefullscreenrequest_wrapper.cpp
+${QtWebEngineCore_GEN_DIR}/qwebengineglobalsettings_wrapper.cpp
+${QtWebEngineCore_GEN_DIR}/qwebengineglobalsettings_dnsmode_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebenginehistory_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebenginehistoryitem_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebenginehistorymodel_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebenginehttprequest_wrapper.cpp
+${QtWebEngineCore_GEN_DIR}/qwebenginenavigationrequest_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebengineloadinginfo_wrapper.cpp
+# FIXME ${QtWebEngineCore_GEN_DIR}/qwebenginemediasourcemodel_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebenginenewwindowrequest_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebenginenotification_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebenginepage_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebenginescript_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebenginescriptcollection_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebenginesettings_wrapper.cpp
-${QtWebEngineCore_GEN_DIR}/qwebenginefindtextresult_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebengineurlrequestinfo_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebengineurlrequestinterceptor_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebengineurlrequestjob_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebengineurlscheme_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebengineurlschemehandler_wrapper.cpp
+${QtWebEngineCore_GEN_DIR}/qwebenginewebauthpinrequest_wrapper.cpp
+${QtWebEngineCore_GEN_DIR}/qwebenginewebauthuxrequest_wrapper.cpp
# module is always needed
${QtWebEngineCore_GEN_DIR}/qtwebenginecore_module_wrapper.cpp
)
${QtWidgets_GEN_DIR}
${QtNetwork_GEN_DIR}
${QtPrintSupport_GEN_DIR}
- ${QtWebChannel_GEN_DIR}
- )
+ ${QtWebChannel_GEN_DIR})
+
set(QtWebEngineCore_libraries pyside6
- ${Qt${QT_MAJOR_VERSION}WebEngineCore_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Gui_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Widgets_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Network_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}PrintSupport_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}WebChannel_LIBRARIES}
- )
+ ${Qt${QT_MAJOR_VERSION}WebEngineCore_LIBRARIES})
set(QtWebEngineCore_deps QtCore QtGui QtNetwork QtPrintSupport QtWebChannel)
// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtWebEngineCore">
+<typesystem package="PySide6.QtWebEngineCore"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/>
<load-typesystem name="QtNetwork/typesystem_network.xml" generate="no"/>
<function signature="qWebEngineChromiumSecurityPatchVersion()"/>
<function signature="qWebEngineVersion()"/>
+ <value-type name="QWebEngineClientCertificateSelection"/>
+ <object-type name="QWebEngineClientCertificateStore"/>
+
<object-type name="QWebEngineCookieStore">
+ <inject-code class="native" position="beginning" file="../glue/qtwebenginecore.cpp"
+ snippet="qwebenginecookiestore-functor"/>
<value-type name="FilterRequest" />
<add-function signature="setCookieFilter(PyCallable* @filterCallback@)">
<inject-code class="target" position="beginning" file="../glue/qtwebenginecore.cpp"
<enum-type name="Roles"/>
</object-type>
+ <object-type name="QWebEngineNavigationRequest">
+ <enum-type name="NavigationType"/>
+ <enum-type name="NavigationRequestAction"/>
+ </object-type>
+
<object-type name="QWebEngineNotification"/>
<object-type name="QWebEnginePage">
<enum-type name="FileSelectionMode"/>
<enum-type name="JavaScriptConsoleMessageLevel"/>
<enum-type name="RenderProcessTerminationStatus"/>
+ <add-function signature="javaScriptPromptPyOverride(QUrl@securityOrigin@,QString@msg@,QString@defaultValue@)"
+ return-type="std::pair<bool,QString>" python-override="true"/>
+ <modify-function signature="javaScriptPrompt(QUrl,QString,QString,QString*)">
+ <inject-code class="shell" position="override" file="../glue/qtwebenginecore.cpp"
+ snippet="qwebenginepage-javascriptprompt-virtual-redirect"/>
+ <modify-argument index="return" pyi-type="Tuple[bool, str]"/>
+ <modify-argument index="4"><remove-default-expression/><remove-argument/></modify-argument>
+ <inject-code class="target" position="beginning" file="../glue/qtwebenginecore.cpp"
+ snippet="qwebenginepage-javascriptprompt-return"/>
+ </modify-function>
<add-function signature="findText(const QString &,QWebEnginePage::FindFlags,PyObject*)">
<inject-code class="target" position="beginning" file="../glue/qtwebenginewidgets.cpp" snippet="qwebenginepage-findtext"/>
</add-function>
<extra-includes>
<include file-name="QtWebEngineCore/QWebEngineNotification" location="global"/>
</extra-includes>
+ <inject-code class="native" position="beginning" file="../glue/qtwebenginecore.cpp"
+ snippet="qwebengineprofile-functor"/>
<enum-type name="HttpCacheType"/>
<enum-type name="PersistentCookiesPolicy"/>
<add-function signature="setNotificationPresenter(PyCallable* @notificationPresenter@)">
<enum-type name="LoadStatus"/>
</value-type>
- <object-type name="QWebEngineRegisterProtocolHandlerRequest"/>
+ <value-type name="QWebEngineRegisterProtocolHandlerRequest"/>
<value-type name="QWebEngineFindTextResult"/>
<object-type name="QWebEngineUrlSchemeHandler"/>
+ <!-- FIXME not in snapshot yet
+ <value-type name="QWebEngineDesktopMediaRequest" since="6.7"/>
+ <object-type name="QWebEngineMediaSourceModel" since="6.7"/>
+ -->
+ <value-type name="QWebEngineWebAuthPinRequest" since="6.7"/>
+ <object-type name="QWebEngineWebAuthUxRequest" since="6.7">
+ <enum-type name="WebAuthUxState"/>
+ <enum-type name="PinEntryReason"/>
+ <enum-type name="PinEntryError"/>
+ <enum-type name="RequestFailureReason"/>
+ </object-type>
+ <namespace-type name="QWebEngineGlobalSettings">
+ <enum-type name="SecureDnsMode"/>
+ <value-type name="DnsMode"/>
+ </namespace-type>
+
<!-- QtQml is pulled in via QtWebEngineCoreDepends. -->
<suppress-warning text="^Scoped enum 'QQml.*' does not have a type entry.*$"/>
${QtWebEngineQuick_SOURCE_DIR}
${QtWebEngineQuick_BINARY_DIR}
${Qt${QT_MAJOR_VERSION}Core_INCLUDE_DIRS}
+ ${Qt${QT_MAJOR_VERSION}Gui_INCLUDE_DIRS}
+ ${Qt${QT_MAJOR_VERSION}Widgets_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Network_INCLUDE_DIRS}
+ ${Qt${QT_MAJOR_VERSION}PrintSupport_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Qml_INCLUDE_DIRS}
+ ${Qt${QT_MAJOR_VERSION}WebChannel_INCLUDE_DIRS}
${libpyside_SOURCE_DIR}
${QtCore_GEN_DIR}
+ ${QtGui_GEN_DIR}
+ ${QtWidgets_GEN_DIR}
${QtNetwork_GEN_DIR}
+ ${QtWebEngineCore_GEN_DIR}
+ ${QtPrintSupport_GEN_DIR}
${QtQml_GEN_DIR}
- )
+ ${QtWebChannel_GEN_DIR})
set(QtWebEngineQuick_libraries pyside6
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Network_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Qml_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}WebEngineQuick_LIBRARIES}
- )
+ ${Qt${QT_MAJOR_VERSION}WebEngineQuick_LIBRARIES})
-set(QtWebEngineQuick_deps QtQml QtNetwork QtCore)
+set(QtWebEngineQuick_deps QtQml QtWebEngineCore)
create_pyside_module(NAME QtWebEngineQuick
INCLUDE_DIRS QtWebEngineQuick_include_dirs
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtWebEngineQuick">
+<typesystem package="PySide6.QtWebEngineQuick"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtQml/typesystem_qml.xml" generate="no"/>
+ <load-typesystem name="QtWebEngineCore/typesystem_webenginecore.xml" generate="no"/>
<namespace-type name="QtWebEngineQuick"/> <!-- initialize() -->
${QtNetwork_GEN_DIR}
${QtWebEngineCore_GEN_DIR}
${QtPrintSupport_GEN_DIR}
- ${QtWebChannel_GEN_DIR}
- )
-set(QtWebEngineWidgets_libraries pyside6
- ${Qt${QT_MAJOR_VERSION}WebEngineWidgets_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Network_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Widgets_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Gui_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}PrintSupport_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}WebChannel_LIBRARIES}
- )
+ ${QtWebChannel_GEN_DIR})
+
+set(QtWebEngineWidgets_libraries pyside6
+ ${Qt${QT_MAJOR_VERSION}WebEngineWidgets_LIBRARIES})
set(QtWebEngineWidgets_deps QtGui QtWidgets QtNetwork QtPrintSupport QtWebChannel QtWebEngineCore)
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtWebEngineWidgets">
+<typesystem package="PySide6.QtWebEngineWidgets"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/>
<load-typesystem name="QtWidgets/typesystem_widgets.xml" generate="no"/>
${libpyside_SOURCE_DIR}
${QtCore_GEN_DIR}
${QtWebSockets_GEN_DIR}
- ${QtNetwork_GEN_DIR}
- )
-set(QtWebSockets_libraries pyside6
- ${Qt${QT_MAJOR_VERSION}WebSockets_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Network_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- )
+ ${QtNetwork_GEN_DIR})
+
+set(QtWebSockets_libraries pyside6
+ ${Qt${QT_MAJOR_VERSION}WebSockets_LIBRARIES})
set(QtWebSockets_deps QtNetwork)
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtWebSockets">
+<typesystem package="PySide6.QtWebSockets"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<load-typesystem name="QtNetwork/typesystem_network.xml" generate="no"/>
${QtWidgets_GEN_DIR}/qproxystyle_wrapper.cpp
${QtWidgets_GEN_DIR}/qpushbutton_wrapper.cpp
${QtWidgets_GEN_DIR}/qradiobutton_wrapper.cpp
+${QtWidgets_GEN_DIR}/qrhiwidget_wrapper.cpp
${QtWidgets_GEN_DIR}/qrubberband_wrapper.cpp
${QtWidgets_GEN_DIR}/qscrollarea_wrapper.cpp
${QtWidgets_GEN_DIR}/qscrollbar_wrapper.cpp
${Qt${QT_MAJOR_VERSION}Widgets_INCLUDE_DIRS}
${libpyside_SOURCE_DIR}
${QtCore_GEN_DIR}
- ${QtGui_GEN_DIR}
- )
+ ${QtGui_GEN_DIR})
+
set(QtWidgets_libraries pyside6
- ${Qt${QT_MAJOR_VERSION}Core_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Gui_LIBRARIES}
- ${Qt${QT_MAJOR_VERSION}Widgets_LIBRARIES}
- )
+ ${Qt${QT_MAJOR_VERSION}Widgets_LIBRARIES})
+
set(QtWidgets_deps QtGui)
create_pyside_module(NAME QtWidgets
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtWidgets">
+<typesystem package="PySide6.QtWidgets"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/>
<load-typesystem name="QtWidgets/typesystem_widgets_common.xml" generate="yes"/>
</typesystem>
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtWidgets">
+<typesystem package="PySide6.QtWidgets"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="templates/core_common.xml" generate="no"/>
<load-typesystem name="templates/widgets_common.xml" generate="no"/>
<function signature="qDrawWinPanel(QPainter*,const QRect&,const QPalette&,bool,const QBrush*)"/>
<function signature="qDrawPlainRect(QPainter*,int,int,int,int,const QColor&,int,const QBrush*)"/>
<function signature="qDrawPlainRect(QPainter*,const QRect&,const QColor&,int,const QBrush*)"/>
+ <function signature="qDrawPlainRoundedRect(QPainter*,int,int,int,int,qreal,qreal,const QColor&,int,const QBrush*)" since="6.7"/>
+ <function signature="qDrawPlainRoundedRect(QPainter*,const QRect&,qreal,qreal,const QColor&,int,const QBrush *)" since="6.7"/>
- <object-type name="QStyleOption" polymorphic-id-expression="%1->type == QStyleOption::SO_Default"
+ <object-type name="QStyleOption" polymorphic-id-expression="%B->type == QStyleOption::SO_Default"
polymorphic-name-function="styleOptionType">
<inject-code class="native" position="beginning" file="../glue/qtwidgets.cpp"
snippet="qstyleoption-typename"/>
<enum-type name="StyleOptionType"/>
<enum-type name="StyleOptionVersion"/>
</object-type>
- <object-type name="QStyleOptionGraphicsItem" polymorphic-id-expression="qstyleoption_cast<const QStyleOptionGraphicsItem *>(%1) != nullptr">
+ <object-type name="QStyleOptionGraphicsItem"
+ polymorphic-id-expression="qstyleoption_cast<const QStyleOptionGraphicsItem *>(%B) != nullptr">
<enum-type name="StyleOptionType"/>
<enum-type name="StyleOptionVersion"/>
</object-type>
- <object-type name="QStyleOptionSizeGrip" polymorphic-id-expression="qstyleoption_cast<const QStyleOptionSizeGrip *>(%1) != nullptr">
+ <object-type name="QStyleOptionSizeGrip"
+ polymorphic-id-expression="qstyleoption_cast<const QStyleOptionSizeGrip *>(%B) != nullptr">
<enum-type name="StyleOptionType"/>
<enum-type name="StyleOptionVersion"/>
</object-type>
- <object-type name="QStyleOptionButton" polymorphic-id-expression="qstyleoption_cast<const QStyleOptionButton *>(%1) != nullptr">
+ <object-type name="QStyleOptionButton"
+ polymorphic-id-expression="qstyleoption_cast<const QStyleOptionButton *>(%B) != nullptr">
<enum-type name="ButtonFeature" flags="ButtonFeatures"/>
<enum-type name="StyleOptionType"/>
<enum-type name="StyleOptionVersion"/>
</object-type>
- <object-type name="QStyleOptionComboBox" polymorphic-id-expression="qstyleoption_cast<const QStyleOptionComboBox *>(%1) != nullptr">
+ <object-type name="QStyleOptionComboBox"
+ polymorphic-id-expression="qstyleoption_cast<const QStyleOptionComboBox *>(%B) != nullptr">
<enum-type name="StyleOptionType"/>
<enum-type name="StyleOptionVersion"/>
</object-type>
- <object-type name="QStyleOptionComplex" polymorphic-id-expression="qstyleoption_cast<const QStyleOptionComplex *>(%1) != nullptr">
+ <object-type name="QStyleOptionComplex"
+ polymorphic-id-expression="qstyleoption_cast<const QStyleOptionComplex *>(%B) != nullptr">
<enum-type name="StyleOptionType"/>
<enum-type name="StyleOptionVersion"/>
</object-type>
- <object-type name="QStyleOptionDockWidget" polymorphic-id-expression="qstyleoption_cast<const QStyleOptionDockWidget *>(%1) != nullptr">
+ <object-type name="QStyleOptionDockWidget"
+ polymorphic-id-expression="qstyleoption_cast<const QStyleOptionDockWidget *>(%B) != nullptr">
<enum-type name="StyleOptionType"/>
<enum-type name="StyleOptionVersion"/>
</object-type>
- <object-type name="QStyleOptionFocusRect" polymorphic-id-expression="qstyleoption_cast<const QStyleOptionFocusRect *>(%1) != nullptr">
+ <object-type name="QStyleOptionFocusRect" polymorphic-id-expression="qstyleoption_cast<const QStyleOptionFocusRect *>(%B) != nullptr">
<enum-type name="StyleOptionType"/>
<enum-type name="StyleOptionVersion"/>
</object-type>
- <object-type name="QStyleOptionFrame" polymorphic-id-expression="qstyleoption_cast<const QStyleOptionFrame *>(%1) != nullptr">
+ <object-type name="QStyleOptionFrame"
+ polymorphic-id-expression="qstyleoption_cast<const QStyleOptionFrame *>(%B) != nullptr">
<enum-type name="FrameFeature" flags="FrameFeatures"/>
<enum-type name="StyleOptionType"/>
<enum-type name="StyleOptionVersion"/>
</object-type>
- <object-type name="QStyleOptionGroupBox" polymorphic-id-expression="qstyleoption_cast<const QStyleOptionGroupBox *>(%1) != nullptr">
+ <object-type name="QStyleOptionGroupBox"
+ polymorphic-id-expression="qstyleoption_cast<const QStyleOptionGroupBox *>(%B) != nullptr">
<enum-type name="StyleOptionType"/>
<enum-type name="StyleOptionVersion"/>
</object-type>
- <object-type name="QStyleOptionHeader" polymorphic-id-expression="qstyleoption_cast<const QStyleOptionHeader *>(%1) != nullptr">
+ <object-type name="QStyleOptionHeader"
+ polymorphic-id-expression="qstyleoption_cast<const QStyleOptionHeader *>(%B) != nullptr">
<enum-type name="SectionPosition"/>
<enum-type name="SelectedPosition"/>
<enum-type name="SortIndicator"/>
<enum-type name="StyleOptionType"/>
<enum-type name="StyleOptionVersion"/>
</object-type>
- <object-type name="QStyleOptionMenuItem" polymorphic-id-expression="qstyleoption_cast<const QStyleOptionMenuItem *>(%1) != nullptr">
+ <object-type name="QStyleOptionMenuItem"
+ polymorphic-id-expression="qstyleoption_cast<const QStyleOptionMenuItem *>(%B) != nullptr">
<enum-type name="CheckType"/>
<enum-type name="MenuItemType"/>
<enum-type name="StyleOptionType"/>
<enum-type name="StyleOptionVersion"/>
</object-type>
- <object-type name="QStyleOptionProgressBar" polymorphic-id-expression="qstyleoption_cast<const QStyleOptionProgressBar *>(%1) != nullptr">
+ <object-type name="QStyleOptionProgressBar"
+ polymorphic-id-expression="qstyleoption_cast<const QStyleOptionProgressBar *>(%B) != nullptr">
<enum-type name="StyleOptionType"/>
<enum-type name="StyleOptionVersion"/>
</object-type>
- <object-type name="QStyleOptionRubberBand" polymorphic-id-expression="qstyleoption_cast<const QStyleOptionRubberBand *>(%1) != nullptr">
+ <object-type name="QStyleOptionRubberBand"
+ polymorphic-id-expression="qstyleoption_cast<const QStyleOptionRubberBand *>(%B) != nullptr">
<enum-type name="StyleOptionType"/>
<enum-type name="StyleOptionVersion"/>
</object-type>
- <object-type name="QStyleOptionSlider" polymorphic-id-expression="qstyleoption_cast<const QStyleOptionSlider *>(%1) != nullptr">
+ <object-type name="QStyleOptionSlider"
+ polymorphic-id-expression="qstyleoption_cast<const QStyleOptionSlider *>(%B) != nullptr">
<enum-type name="StyleOptionType"/>
<enum-type name="StyleOptionVersion"/>
</object-type>
- <object-type name="QStyleOptionSpinBox" polymorphic-id-expression="qstyleoption_cast<const QStyleOptionSpinBox *>(%1) != nullptr">
+ <object-type name="QStyleOptionSpinBox"
+ polymorphic-id-expression="qstyleoption_cast<const QStyleOptionSpinBox *>(%B) != nullptr">
<enum-type name="StyleOptionType"/>
<enum-type name="StyleOptionVersion"/>
</object-type>
- <object-type name="QStyleOptionTab" polymorphic-id-expression="qstyleoption_cast<const QStyleOptionTab *>(%1) != nullptr">
+ <object-type name="QStyleOptionTab"
+ polymorphic-id-expression="qstyleoption_cast<const QStyleOptionTab *>(%B) != nullptr">
<enum-type name="CornerWidget" flags="CornerWidgets"/>
<enum-type name="SelectedPosition"/>
<enum-type name="StyleOptionType"/>
<enum-type name="TabFeature" flags="TabFeatures"/>
<enum-type name="TabPosition"/>
</object-type>
- <object-type name="QStyleOptionTabBarBase" polymorphic-id-expression="qstyleoption_cast<const QStyleOptionTabBarBase *>(%1) != nullptr">
+ <object-type name="QStyleOptionTabBarBase"
+ polymorphic-id-expression="qstyleoption_cast<const QStyleOptionTabBarBase *>(%B) != nullptr">
<enum-type name="StyleOptionType"/>
<enum-type name="StyleOptionVersion"/>
</object-type>
- <object-type name="QStyleOptionTabWidgetFrame" polymorphic-id-expression="qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(%1) != nullptr">
+ <object-type name="QStyleOptionTabWidgetFrame"
+ polymorphic-id-expression="qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(%B) != nullptr">
<enum-type name="StyleOptionType"/>
<enum-type name="StyleOptionVersion"/>
</object-type>
- <object-type name="QStyleOptionTitleBar" polymorphic-id-expression="qstyleoption_cast<const QStyleOptionTitleBar *>(%1) != nullptr">
+ <object-type name="QStyleOptionTitleBar" polymorphic-id-expression="qstyleoption_cast<const QStyleOptionTitleBar *>(%B) != nullptr">
<enum-type name="StyleOptionType"/>
<enum-type name="StyleOptionVersion"/>
</object-type>
- <object-type name="QStyleOptionToolBar" polymorphic-id-expression="qstyleoption_cast<const QStyleOptionToolBar *>(%1) != nullptr">
+ <object-type name="QStyleOptionToolBar"
+ polymorphic-id-expression="qstyleoption_cast<const QStyleOptionToolBar *>(%B) != nullptr">
<enum-type name="StyleOptionType"/>
<enum-type name="StyleOptionVersion"/>
<enum-type name="ToolBarFeature" flags="ToolBarFeatures"/>
<enum-type name="ToolBarPosition"/>
</object-type>
- <object-type name="QStyleOptionToolBox" polymorphic-id-expression="qstyleoption_cast<const QStyleOptionToolBox *>(%1) != nullptr">
+ <object-type name="QStyleOptionToolBox"
+ polymorphic-id-expression="qstyleoption_cast<const QStyleOptionToolBox *>(%B) != nullptr">
<enum-type name="SelectedPosition"/>
<enum-type name="StyleOptionType"/>
<enum-type name="StyleOptionVersion"/>
<enum-type name="TabPosition"/>
</object-type>
- <object-type name="QStyleOptionToolButton" polymorphic-id-expression="qstyleoption_cast<const QStyleOptionToolButton *>(%1) != nullptr">
+ <object-type name="QStyleOptionToolButton" polymorphic-id-expression="qstyleoption_cast<const QStyleOptionToolButton *>(%B) != nullptr">
<enum-type name="StyleOptionType"/>
<enum-type name="StyleOptionVersion"/>
<enum-type name="ToolButtonFeature" flags="ToolButtonFeatures"/>
</object-type>
- <value-type name="QStyleOptionViewItem" polymorphic-id-expression="%1->type == QStyleOptionViewItem::Type && %1->version == QStyleOptionViewItem::Version">
+ <value-type name="QStyleOptionViewItem"
+ polymorphic-id-expression="%B->type == QStyleOptionViewItem::Type && %B->version == QStyleOptionViewItem::Version">
<enum-type name="Position"/>
<enum-type name="StyleOptionType"/>
<enum-type name="StyleOptionVersion"/>
<object-type name="QDateEdit"/>
<object-type name="QDialog">
<enum-type name="DialogCode" python-type="IntEnum"/>
- <modify-function signature="exec()" allow-thread="yes"/>
+ <modify-function signature="exec()" allow-thread="yes">
+ <inject-code file="../glue/qtwidgets.cpp" snippet="qdialog-exec-remove-parent-relation"/>
+ </modify-function>
<add-function signature="exec_()" return-type="int">
<inject-code file="../glue/qtwidgets.cpp" snippet="qapplication-exec"/>
</add-function>
</modify-argument>
</modify-function>
</object-type>
- <object-type name="QWidgetItem" polymorphic-id-expression="%1->widget()"/>
+ <object-type name="QWidgetItem" polymorphic-id-expression="%B->widget()"/>
- <object-type name="QGraphicsSceneContextMenuEvent" copyable="false" polymorphic-id-expression="%1->type() == QEvent::GraphicsSceneContextMenu">
+ <object-type name="QGraphicsSceneContextMenuEvent"
+ polymorphic-id-expression="%B->type() == QEvent::GraphicsSceneContextMenu">
<enum-type name="Reason"/>
</object-type>
- <object-type name="QGraphicsSceneDragDropEvent" copyable="false" polymorphic-id-expression="%1->type() == QEvent::GraphicsSceneDragEnter || %1->type() == QEvent::GraphicsSceneDragLeave || %1->type() == QEvent::GraphicsSceneDragMove || %1->type() == QEvent::GraphicsSceneDrop" >
+ <object-type name="QGraphicsSceneDragDropEvent"
+ polymorphic-id-expression="%B->type() == QEvent::GraphicsSceneDragEnter || %B->type() == QEvent::GraphicsSceneDragLeave || %B->type() == QEvent::GraphicsSceneDragMove || %B->type() == QEvent::GraphicsSceneDrop" >
<!-- ### "setMimeData(const QMimeData*)" is an internal method. -->
<modify-function signature="setMimeData(const QMimeData*)" remove="all"/>
<!-- ### "setSource(QWidget*)" is an internal method. -->
<!-- ### "setWidget(QWidget*)" is an internal method. -->
<modify-function signature="setWidget(QWidget*)" remove="all"/>
</object-type>
- <object-type name="QGraphicsSceneMoveEvent" copyable="false" polymorphic-id-expression="%1->type() == QEvent::GraphicsSceneMove"/>
- <object-type name="QGraphicsSceneResizeEvent" copyable="false" polymorphic-id-expression="%1->type() == QEvent::GraphicsSceneResize"/>
- <object-type name="QGraphicsSceneHelpEvent" copyable="false" polymorphic-id-expression="%1->type() == QEvent::GraphicsSceneHelp"/>
- <object-type name="QGraphicsSceneHoverEvent" copyable="false" polymorphic-id-expression="%1->type() == QEvent::GraphicsSceneHoverEnter || %1->type() == QEvent::GraphicsSceneHoverLeave || %1->type() == QEvent::GraphicsSceneHoverMove"/>
- <object-type name="QGraphicsSceneMouseEvent" copyable="false" polymorphic-id-expression="%1->type() == QEvent::GraphicsSceneMouseDoubleClick || %1->type() == QEvent::GraphicsSceneMouseMove || %1->type() == QEvent::GraphicsSceneMousePress || %1->type() == QEvent::GraphicsSceneMouseRelease"/>
- <object-type name="QGraphicsSceneWheelEvent" copyable="false" polymorphic-id-expression="%1->type() == QEvent::GraphicsSceneWheel"/>
-
- <object-type name="QGestureEvent" polymorphic-id-expression="%1->type() == QEvent::Gesture || %1->type() == QEvent::GestureOverride" since="4.6">
+ <object-type name="QGraphicsSceneMoveEvent"
+ polymorphic-id-expression="%B->type() == QEvent::GraphicsSceneMove"/>
+ <object-type name="QGraphicsSceneResizeEvent"
+ polymorphic-id-expression="%B->type() == QEvent::GraphicsSceneResize"/>
+ <object-type name="QGraphicsSceneHelpEvent"
+ polymorphic-id-expression="%B->type() == QEvent::GraphicsSceneHelp"/>
+ <object-type name="QGraphicsSceneHoverEvent"
+ polymorphic-id-expression="%B->type() == QEvent::GraphicsSceneHoverEnter || %B->type() == QEvent::GraphicsSceneHoverLeave || %B->type() == QEvent::GraphicsSceneHoverMove"/>
+ <object-type name="QGraphicsSceneMouseEvent"
+ polymorphic-id-expression="%B->type() == QEvent::GraphicsSceneMouseDoubleClick || %B->type() == QEvent::GraphicsSceneMouseMove || %B->type() == QEvent::GraphicsSceneMousePress || %B->type() == QEvent::GraphicsSceneMouseRelease"/>
+ <object-type name="QGraphicsSceneWheelEvent"
+ polymorphic-id-expression="%B->type() == QEvent::GraphicsSceneWheel"/>
+
+ <object-type name="QGestureEvent"
+ polymorphic-id-expression="%B->type() == QEvent::Gesture || %B->type() == QEvent::GestureOverride" since="4.6">
<modify-function signature="activeGestures()const">
<modify-argument index="return">
<define-ownership owner="default"/>
<modify-argument index="return">
<define-ownership owner="default"/>
</modify-argument>
- <inject-code class="target" position="end" file="../glue/qtwidgets.cpp" snippet="addownership-0"/>
+ <inject-code class="target" position="end" file="../glue/qtwidgets.cpp"
+ snippet="addownership-item-at"/>
</modify-function>
<modify-function signature="removeWidget(QWidget*)">
<modify-argument index="return">
<define-ownership owner="default"/>
</modify-argument>
- <inject-code class="target" position="end" file="../glue/qtwidgets.cpp" snippet="addownership-0"/>
+ <inject-code class="target" position="end" file="../glue/qtwidgets.cpp"
+ snippet="addownership-item-at"/>
</modify-function>
<modify-function signature="addWidget(QWidget*,int,int,QFlags<Qt::AlignmentFlag>)">
<modify-argument index="4">
<object-type name="QCommandLinkButton"/>
<!-- FIXME PYSIDE7: Move to QtGui -->
- <object-type name="QFileSystemModel" polymorphic-id-expression="qobject_cast<QFileSystemModel*>(%1)">
+ <object-type name="QFileSystemModel" polymorphic-id-expression="qobject_cast<QFileSystemModel*>(%B)">
<enum-type name="Roles" python-type="IntEnum"/>
<enum-type name="Option" flags="Options"/>
<modify-function signature="setIconProvider(QAbstractFileIconProvider*)">
<object-type name="QPinchGesture" since="4.6">
<enum-type name="ChangeFlag" flags="ChangeFlags"/>
</object-type>
+
+ <object-type name="QRhiWidget" since="6.7">
+ <enum-type name="Api"/>
+ <enum-type name="TextureFormat"/>
+ </object-type>
+
<object-type name="QSwipeGesture" since="4.6">
<enum-type name="SwipeDirection"/>
</object-type>
${QtXml_GEN_DIR}/qdomcharacterdata_wrapper.cpp
${QtXml_GEN_DIR}/qdomcomment_wrapper.cpp
${QtXml_GEN_DIR}/qdomdocument_wrapper.cpp
+${QtXml_GEN_DIR}/qdomdocument_parseresult_wrapper.cpp
${QtXml_GEN_DIR}/qdomdocumentfragment_wrapper.cpp
${QtXml_GEN_DIR}/qdomdocumenttype_wrapper.cpp
${QtXml_GEN_DIR}/qdomelement_wrapper.cpp
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtXml">
+<typesystem package="PySide6.QtXml"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no" />
<load-typesystem name="templates/core_common.xml" generate="no" />
<value-type name="QDomDocument">
<enum-type name="ParseOption" flags="ParseOptions" since="6.5"/>
<!-- will be replaced in inject code -->
+
+ <value-type name="ParseResult"/>
+
<modify-function signature="setContent(const QByteArray&,bool,QString*,int*,int*)">
<modify-argument index="3">
<remove-argument/>
--- /dev/null
+// @snippet qmlregistersingletoninstance
+.. py:function:: qmlRegisterSingletonInstance(pytype: type,\
+ uri: str,\
+ versionMajor: int,\
+ versionMinor: int,\
+ typeName: str,\
+ instanceObject: object) -> int
+
+ :param type pytype: Python class
+ :param str uri: uri to use while importing the component in QML
+ :param int versionMajor: major version
+ :param int versionMinor: minor version
+ :param str typeName: name exposed to QML
+ :param object instanceObject: singleton object to be registered
+ :return: int (the QML type id)
+
+This function registers a singleton Python object *instanceObject*, with a
+particular *uri* and *typeName*. Its version is a combination of *versionMajor*
+and *versionMinor*. Use this function to register an object of the given type
+*pytype* as a singleton type.
+// @snippet qmlregistersingletoninstance
+
+// @snippet qmlregistersingletontype_qobject_nocallback
+.. py:function:: qmlRegisterSingletonType(pytype: type, uri: str, versionMajor: int, versionMinor: int, typeName: str) -> int
+
+ :param type pytype: Python class
+ :param str uri: uri to use while importing the component in QML
+ :param int versionMajor: major version
+ :param int versionMinor: minor version
+ :param str typeName: name exposed to QML
+ :return: int (the QML type id)
+
+This function registers a Python type as a singleton in the QML system.
+
+Alternatively, the :ref:`QmlSingleton` decorator can be used.
+// @snippet qmlregistersingletontype_qobject_nocallback
+
+// @snippet qmlregistersingletontype_qobject_callback
+.. py:function:: qmlRegisterSingletonType(pytype: type, uri: str, versionMajor: int, versionMinor: int, typeName: str, callback: object) -> int
+
+ :param type pytype: Python class
+ :param str uri: uri to use while importing the component in QML
+ :param int versionMajor: major version
+ :param int versionMinor: minor version
+ :param str typeName: name exposed to QML
+ :param object callback: Python callable (to handle Python type)
+ :return: int (the QML type id)
+
+This function registers a Python type as a singleton in the QML system using
+the provided callback (which gets a QQmlEngine as a parameter) to generate the
+singleton.
+// @snippet qmlregistersingletontype_qobject_callback
+
+// @snippet qmlregistersingletontype_qjsvalue
+.. py:function:: qmlRegisterSingletonType(uri: str, versionMajor: int, versionMinor: int, typeName: str, callback: object) -> int
+
+ :param str uri: uri to use while importing the component in QML
+ :param int versionMajor: major version
+ :param int versionMinor: minor version
+ :param str typeName: name exposed to QML
+ :param object callback: Python callable (to handle QJSValue)
+ :return: int (the QML type id)
+
+This function registers a QJSValue as a singleton in the QML system using the
+provided callback (which gets a QQmlEngine as a parameter) to generate the
+singleton.
+// @snippet qmlregistersingletontype_qjsvalue
+
+// @snippet qmlregistertype
+.. py:function:: qmlRegisterType(pytype: type, uri: str, versionMajor: int, versionMinor: int, qmlName: str) -> int
+
+ :param type pytype: Python class
+ :param str uri: uri to use while importing the component in QML
+ :param int versionMajor: major version
+ :param int versionMinor: minor version
+ :param str qmlName: name exposed to QML
+ :return: int (the QML type id)
+
+This function registers the Python *type* in the QML system with the name
+*qmlName*, in the library imported from *uri* having the version number
+composed from *versionMajor* and *versionMinor*. For example, this registers a
+Python class 'MySliderItem' as a QML type named 'Slider' for version '1.0' of a
+module called 'com.mycompany.qmlcomponents':
+
+ ::
+
+ qmlRegisterType(MySliderItem, "com.mycompany.qmlcomponents", 1, 0, "Slider")
+
+Once this is registered, the type can be used in QML by importing the specified
+module name and version number:
+
+ ::
+
+ import com.mycompany.qmlcomponents 1.0
+
+ Slider { ... }
+
+Note that it's perfectly reasonable for a library to register types to older
+versions than the actual version of the library. Indeed, it is normal for the
+new library to allow QML written to previous versions to continue to work, even
+if more advanced versions of some of its types are available.
+// @snippet qmlregistertype
+
+// @snippet qmlregisteruncreatabletype
+.. py:function:: qmlRegisterUncreatableType(pytype: type, uri: str, versionMajor: int, versionMinor: int, qmlName: str, noCreationReason: str) -> int
+
+ :param type pytype: Python class
+ :param str uri: uri to use while importing the component in QML
+ :param int versionMajor: major version
+ :param int versionMinor: minor version
+ :param str qmlName: name exposed to QML
+ :param str noCreationReason: Error message shown when trying to create the QML type
+ :return: int (the QML type id)
+
+This function registers the Python *type* in the QML system as an uncreatable
+type with the name *qmlName*, in the library imported from *uri* having the
+version number composed from *versionMajor* and *versionMinor*, showing
+*noCreationReason* as an error message when creating the type is attempted. For
+example, this registers a Python class 'MySliderItem' as a QML type named
+'Slider' for version '1.0' of a module called 'com.mycompany.qmlcomponents':
+
+ ::
+ qmlRegisterUncreatableType(MySliderItem, "com.mycompany.qmlcomponents", 1, 0, "Slider", "Slider cannot be created.")
+
+Note that it's perfectly reasonable for a library to register types to older
+versions than the actual version of the library. Indeed, it is normal for the
+new library to allow QML written to previous versions to continue to work, even
+if more advanced versions of some of its types are available.
+
+Alternatively, the :ref:`QmlUncreatable` decorator can be used.
+// @snippet qmlregisteruncreatabletype
+
+// @snippet qqmlengine-singletoninstance-qmltypeid
+Returns the instance of a singleton type that was registered under qmlTypeId.
+For ``QObject``-derived singleton types, the ``QObject`` instance is returned,
+otherwise a ``QJSValue`` or ``None``.
+
+It is recommended to store the QML type id, e.g. as a static member in the
+singleton class. The lookup via qmlTypeId() is costly.
+// @snippet qqmlengine-singletoninstance-qmltypeid
+
+// @snippet qqmlengine-singletoninstance-typename Returns the instance of a
+singleton type named typeName from the module specified by uri.
+For ``QObject``-derived singleton types, the ``QObject`` instance is returned,
+otherwise a ``QJSValue`` or ``None``.
+
+This method can be used as an alternative to calling qmlTypeId followed by the
+id based overload of singletonInstance. This is convenient when one only needs
+to do a one time setup of a singleton; if repeated access to the singleton is
+required, caching its typeId will allow faster subsequent access via the
+type-id based overload.
+// @snippet qqmlengine-singletoninstance-typename
--- /dev/null
+// @snippet quick_test_main_documentation
+
+Sets up the entry point for a Qt Quick Test application.
+The ``name`` argument uniquely identifies this set of tests.
+
+``sys.argv`` should be passed to the ``argv`` argument to ensure
+propagation of the command line arguments.
+
+.. note:: The function assumes that your test sources are in the current
+ directory, unless the ``QUICK_TEST_SOURCE_DIR`` environment
+ variable is set or a directory is passed in ``dir``.
+
+The following snippet demonstrates the use of this function:
+
+.. code-block:: Python
+
+ import sys
+ from PySide6.QtQuickTest import QUICK_TEST_MAIN
+
+ ex = QUICK_TEST_MAIN("example", sys.argv)
+ sys.exit(ex)
+
+
+// @snippet quick_test_main_documentation
+
+// @snippet quick_test_main_with_setup_documentation
+
+Sets up the entry point for a Qt Quick Test application.
+The ``name`` argument uniquely identifies this set of tests.
+
+``sys.argv`` should be passed to the ``argv`` argument to ensure
+propagation of the command line arguments.
+
+This function is identical to ``QUICK_TEST_MAIN()``, except that it takes an
+additional argument ``setup``, the type of a ``QObject``-derived
+class which will be instantiated. With this class, it is possible to define
+additional setup code to execute before running the QML test.
+
+The following snippet demonstrates the use of this function:
+
+.. code-block:: Python
+
+ import sys
+ from PySide6.QtQuickTest import QUICK_TEST_MAIN_WITH_SETUP
+
+ class CustomTestSetup(QObject):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+
+ @Slot(QQmlEngine)
+ def qmlEngineAvailable(self, qmlEngine):
+ pass
+
+ ex = QUICK_TEST_MAIN_WITH_SETUP("qquicktestsetup", CustomTestSetup, sys.argv)
+ sys.exit(ex)
+
+
+.. note:: The function assumes that your test sources are in the current
+ directory, unless the ``QUICK_TEST_SOURCE_DIR`` environment
+ variable is set or a directory is passed in ``dir``.
+
+// @snippet quick_test_main_with_setup_documentation
--- /dev/null
+// @snippet quiloader-registercustomwidget
+Registers a Python created custom widget to QUiLoader, so it can be recognized
+when loading a `.ui` file. The custom widget type is passed via the
+``customWidgetType`` argument. This is needed when you want to override a
+virtual method of some widget in the interface, since duck punching will not
+work with widgets created by QUiLoader based on the contents of the `.ui` file.
+
+(Remember that
+`duck punching virtual methods is an invitation for your own demise! <https://doc.qt.io/qtforpython/shiboken6/wordsofadvice.html#duck-punching-and-virtual-methods>`_)
+
+Let's see an obvious example. If you want to create a new widget it's probable you'll end up
+overriding :class:`~PySide6.QtGui.QWidget`'s :meth:`~PySide6.QtGui.QWidget.paintEvent` method.
+
+.. code-block:: python
+
+ class Circle(QWidget):
+ def paintEvent(self, event):
+ with QPainter(self) as painter:
+ painter.setPen(self.pen)
+ painter.setBrush(QBrush(self.color))
+ painter.drawEllipse(event.rect().center(), 20, 20)
+
+ # ...
+
+ loader = QUiLoader()
+ loader.registerCustomWidget(Circle)
+ circle = loader.load('circle.ui')
+ circle.show()
+
+ # ...
+// @snippet quiloader-registercustomwidget
+
+// @snippet loaduitype
+.. currentmodule:: PySide6.QtUiTools
+
+loadUiType
+***********
+.. py:function:: loadUiType(uifile: str) -> tuple(object, object)
+
+ :param str uifile: The name of the `.ui` file
+ :return: tuple(object, object)
+
+This function generates and loads a `.ui` file at runtime, and it returns
+a `tuple` containing the reference to the Python class, and the base class.
+
+We recommend not to use this approach as the workflow should be to generate a Python file
+from the `.ui` file, and then import and load it to use it, but we do understand that
+there are some corner cases when such functionality is required.
+
+The internal process relies on `uic` being in the PATH.
+The `pyside6-uic` wrapper uses a shipped `uic` that is located in the
+`site-packages/PySide6/uic`, so PATH needs to be updated to use that if there
+is no `uic` in the system.
+
+A simple use case is::
+
+ from PySide6.QtUiTools import loadUiType
+
+ generated_class, base_class = loadUiType("themewidget.ui")
+ # the values will be:
+ # (<class '__main__.Ui_ThemeWidgetForm'>, <class 'PySide6.QtWidgets.QWidget'>)
+
+ widget = base_class()
+ form = generated_class()
+ form.setupUi(widget)
+ # form.a_widget_member.a_method_of_member()
+ widget.show()
+// @snippet loaduitype
return PyFloat_FromDouble(out.toFloat());
case QMetaType::Bool:
if (out.toBool()) {
- Py_INCREF(Py_True);
- return Py_True;
+ Py_RETURN_TRUE;
}
- Py_INCREF(Py_False);
- return Py_False;
+ Py_RETURN_FALSE;
default:
break;
}
}
return QVariant(lst);
}
+
+using SpecificConverter = Shiboken::Conversions::SpecificConverter;
+
+static std::optional<SpecificConverter> converterForQtType(const char *typeNameC)
+{
+ // Fix typedef "QGenericMatrix<3,3,float>" -> QMatrix3x3". The reverse
+ // conversion happens automatically in QMetaType::fromName() in
+ // QVariant_resolveMetaType().
+ QByteArrayView typeNameV(typeNameC);
+ if (typeNameV.startsWith("QGenericMatrix<") && typeNameV.endsWith(",float>")) {
+ QByteArray typeName = typeNameV.toByteArray();
+ typeName.remove(1, 7);
+ typeName.remove(7, 1); // '<'
+ typeName.chop(7);
+ typeName.replace(',', 'x');
+ SpecificConverter matrixConverter(typeName.constData());
+ if (matrixConverter)
+ return matrixConverter;
+ }
+ SpecificConverter converter(typeNameC);
+ if (converter)
+ return converter;
+ return std::nullopt;
+}
// @snippet qvariant-conversion
// @snippet qt-qabs
qAddPostRoutine(PySide::globalPostRoutineCallback);
// @snippet qt-qaddpostroutine
+// @snippet qcompress-buffer
+auto *ptr = reinterpret_cast<uchar*>(Shiboken::Buffer::getPointer(%PYARG_1));
+QByteArray compressed = %FUNCTION_NAME(ptr, %2, %3);
+%PYARG_0 = %CONVERTTOPYTHON[QByteArray](compressed);
+// @snippet qcompress-buffer
+
+// @snippet quncompress-buffer
+auto *ptr = reinterpret_cast<uchar*>(Shiboken::Buffer::getPointer(%PYARG_1));
+QByteArray uncompressed = %FUNCTION_NAME(ptr, %2);
+%PYARG_0 = %CONVERTTOPYTHON[QByteArray](uncompressed);
+// @snippet quncompress-buffer
+
// @snippet qt-version
QList<QByteArray> version = QByteArray(qVersion()).split('.');
PyObject *pyQtVersion = PyTuple_New(3);
// @snippet qt-init-feature
// @snippet qt-pysideinit
-Shiboken::Conversions::registerConverterName(SbkPySide6_QtCoreTypeConverters[SBK_QSTRING_IDX], "unicode");
-Shiboken::Conversions::registerConverterName(SbkPySide6_QtCoreTypeConverters[SBK_QSTRING_IDX], "str");
-Shiboken::Conversions::registerConverterName(SbkPySide6_QtCoreTypeConverters[SBK_QTCORE_QLIST_QVARIANT_IDX], "QVariantList");
-Shiboken::Conversions::registerConverterName(SbkPySide6_QtCoreTypeConverters[SBK_QTCORE_QMAP_QSTRING_QVARIANT_IDX], "QVariantMap");
+Shiboken::Conversions::registerConverterName(SbkPySide6_QtCoreTypeConverters[SBK_QString_IDX], "unicode");
+Shiboken::Conversions::registerConverterName(SbkPySide6_QtCoreTypeConverters[SBK_QString_IDX], "str");
+Shiboken::Conversions::registerConverterName(SbkPySide6_QtCoreTypeConverters[SBK_QtCore_QList_QVariant_IDX], "QVariantList");
+Shiboken::Conversions::registerConverterName(SbkPySide6_QtCoreTypeConverters[SBK_QtCore_QMap_QString_QVariant_IDX], "QVariantMap");
PySide::registerInternalQtConf();
PySide::init(module);
%PYARG_0 = PyDateTime_FromDateAndTime(date.year(), date.month(), date.day(), time.hour(), time.minute(), time.second(), time.msec()*1000);
// @snippet qdatetime-topython
-// @snippet qpoint
-namespace PySide {
- template<> inline Py_ssize_t hash(const QPoint &v) {
- return qHash(qMakePair(v.x(), v.y()));
- }
-};
-// @snippet qpoint
-
-// @snippet qrect
-namespace PySide {
- template<> inline Py_ssize_t hash(const QRect &r) {
- const int v[4] = {r.x(), r.y(), r.width(), r.height()};
- return qHashRange(v, v + 4);
- }
-};
-// @snippet qrect
-
-// @snippet qsize
-namespace PySide {
- template<> inline Py_ssize_t hash(const QSize &v) {
- return qHash(qMakePair(v.width(), v.height()));
- }
-};
-// @snippet qsize
-
// @snippet qtime-topython
if (!PyDateTimeAPI)
PyDateTime_IMPORT;
// @snippet qbytearray-mgetitem
if (PyIndex_Check(_key)) {
const Py_ssize_t _i = PyNumber_AsSsize_t(_key, PyExc_IndexError);
- if (_i < 0 || _i >= %CPPSELF.size()) {
- PyErr_SetString(PyExc_IndexError, "index out of bounds");
- return nullptr;
- }
+ if (_i < 0 || _i >= %CPPSELF.size())
+ return PyErr_Format(PyExc_IndexError, "index out of bounds");
char res[2] = {%CPPSELF.at(_i), '\0'};
return PyBytes_FromStringAndSize(res, 1);
}
-if (PySlice_Check(_key) == 0) {
- PyErr_Format(PyExc_TypeError,
+if (PySlice_Check(_key) == 0)
+ return PyErr_Format(PyExc_TypeError,
"list indices must be integers or slices, not %.200s",
Py_TYPE(_key)->tp_name);
- return nullptr;
-}
Py_ssize_t start, stop, step, slicelength;
if (PySlice_GetIndicesEx(_key, %CPPSELF.size(), &start, &stop, &step, &slicelength) < 0)
// @snippet qbytearray-mgetitem
// @snippet qbytearray-msetitem
+// PYSIDE-2404: Usage of the `get()` function not necessary, the type exists.
if (PyIndex_Check(_key)) {
Py_ssize_t _i = PyNumber_AsSsize_t(_key, PyExc_IndexError);
if (_i == -1 && PyErr_Occurred())
PyErr_SetString(PyExc_ValueError, "bytearray must be of size 1");
return -1;
}
- } else if (Py_TYPE(_value) == reinterpret_cast<PyTypeObject *>(SbkPySide6_QtCoreTypes[SBK_QBYTEARRAY_IDX])) {
+ } else if (Py_TYPE(_value) == reinterpret_cast<PyTypeObject *>(
+ SbkPySide6_QtCoreTypeStructs[SBK_QByteArray_IDX].type)) {
if (PyObject_Length(_value) != 1) {
PyErr_SetString(PyExc_ValueError, "QByteArray must be of size 1");
return -1;
Py_ssize_t value_length = 0;
if (_value != nullptr && _value != Py_None) {
if (!(PyBytes_Check(_value) || PyByteArray_Check(_value)
- || Py_TYPE(_value) == reinterpret_cast<PyTypeObject *>(SbkPySide6_QtCoreTypes[SBK_QBYTEARRAY_IDX]))) {
+ || Py_TYPE(_value) == SbkPySide6_QtCoreTypeStructs[SBK_QByteArray_IDX].type)) {
PyErr_Format(PyExc_TypeError, "bytes, bytearray or QByteArray is required, not %.200s",
Py_TYPE(_value)->tp_name);
return -1;
view->len = cppSelf->size();
view->readonly = 0;
view->itemsize = 1;
- view->format = const_cast<char *>("c");
+ view->format = (flags & PyBUF_FORMAT) == PyBUF_FORMAT ? const_cast<char *>("B") : nullptr;
view->ndim = 1;
view->shape = (flags & PyBUF_ND) == PyBUF_ND ? &(view->len) : nullptr;
- view->strides = &view->itemsize;
+ view->strides = (flags & PyBUF_STRIDES) == PyBUF_STRIDES ? &(view->itemsize) : nullptr;
view->suboffsets = nullptr;
view->internal = nullptr;
%PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0);
// @snippet qtranslator-load
-// @snippet qtimer-singleshot-1
-// %FUNCTION_NAME() - disable generation of c++ function call
-(void) %2; // remove warning about unused variable
-Shiboken::AutoDecRef emptyTuple(PyTuple_New(0));
-auto *timerType = Shiboken::SbkType<QTimer>();
-auto *pyTimer = timerType->tp_new(Shiboken::SbkType<QTimer>(), emptyTuple, nullptr);
-timerType->tp_init(pyTimer, emptyTuple, nullptr);
-
-auto timer = %CONVERTTOCPP[QTimer *](pyTimer);
-Shiboken::AutoDecRef result(
- PyObject_CallMethod(pyTimer, "connect", "OsOs",
- pyTimer,
- SIGNAL(timeout()),
- %PYARG_2,
- %3)
-);
-Shiboken::Object::releaseOwnership(reinterpret_cast<SbkObject *>(pyTimer));
-Py_XDECREF(pyTimer);
-timer->setSingleShot(true);
-timer->connect(timer, &QTimer::timeout, timer, &QObject::deleteLater);
-timer->start(%1);
-// @snippet qtimer-singleshot-1
-
-// @snippet qtimer-singleshot-2
-// %FUNCTION_NAME() - disable generation of c++ function call
+// @snippet qtimer-singleshot-functorclass
+struct QSingleShotTimerFunctor : public Shiboken::PyObjectHolder
+{
+public:
+ using Shiboken::PyObjectHolder::PyObjectHolder;
+
+ void operator()();
+};
+
+void QSingleShotTimerFunctor::operator()()
+{
+ Shiboken::GilState state;
+ Shiboken::AutoDecRef arglist(PyTuple_New(0));
+ Shiboken::AutoDecRef ret(PyObject_CallObject(object(), arglist));
+ if (Shiboken::Errors::occurred())
+ Shiboken::Errors::storeErrorOrPrint();
+ release(); // single shot
+}
+// @snippet qtimer-singleshot-functorclass
+
+// @snippet qtimer-singleshot-direct-mapping
Shiboken::AutoDecRef emptyTuple(PyTuple_New(0));
-auto *timerType = Shiboken::SbkType<QTimer>();
-auto *pyTimer = timerType->tp_new(Shiboken::SbkType<QTimer>(), emptyTuple, nullptr);
-timerType->tp_init(pyTimer, emptyTuple, nullptr);
-QTimer * timer = %CONVERTTOCPP[QTimer *](pyTimer);
-timer->setSingleShot(true);
-
-if (PyObject_TypeCheck(%2, PySideSignalInstance_TypeF())) {
- PySideSignalInstance *signalInstance = reinterpret_cast<PySideSignalInstance *>(%2);
- Shiboken::AutoDecRef signalSignature(Shiboken::String::fromFormat("2%s", PySide::Signal::getSignature(signalInstance)));
- Shiboken::AutoDecRef result(
- PyObject_CallMethod(pyTimer, "connect", "OsOO",
- pyTimer,
- SIGNAL(timeout()),
- PySide::Signal::getObject(signalInstance),
- signalSignature.object())
- );
+%CPPSELF.%FUNCTION_NAME(%1, %2, %3);
+// @snippet qtimer-singleshot-direct-mapping
+
+// @snippet qtimer-singleshot-functor
+auto msec = %1;
+if (msec == 0) {
+ if (PyObject_TypeCheck(%2, PySideSignalInstance_TypeF())) {
+ auto *signal = %PYARG_2;
+ auto cppCallback = [signal]()
+ {
+ Shiboken::GilState state;
+ Shiboken::AutoDecRef ret(PyObject_CallMethod(signal, "emit", "()"));
+ Py_DECREF(signal);
+ };
+
+ Py_INCREF(signal);
+ %CPPSELF.%FUNCTION_NAME(msec, cppCallback);
+ } else {
+ %CPPSELF.%FUNCTION_NAME(msec, QSingleShotTimerFunctor(%PYARG_2));
+ }
} else {
- Shiboken::AutoDecRef result(
- PyObject_CallMethod(pyTimer, "connect", "OsO",
- pyTimer,
- SIGNAL(timeout()),
- %PYARG_2)
- );
-}
+ // %FUNCTION_NAME() - disable generation of c++ function call
+ Shiboken::AutoDecRef emptyTuple(PyTuple_New(0));
+ auto *timerType = Shiboken::SbkType<QTimer>();
+ auto newFunc = reinterpret_cast<newfunc>(PepType_GetSlot(timerType, Py_tp_new));
+ auto initFunc = reinterpret_cast<initproc>(PepType_GetSlot(timerType, Py_tp_init));
+ auto *pyTimer = newFunc(Shiboken::SbkType<QTimer>(), emptyTuple, nullptr);
+ initFunc(pyTimer, emptyTuple, nullptr);
-timer->connect(timer, &QTimer::timeout, timer, &QObject::deleteLater, Qt::DirectConnection);
-Shiboken::Object::releaseOwnership(reinterpret_cast<SbkObject *>(pyTimer));
-Py_XDECREF(pyTimer);
-timer->start(%1);
-// @snippet qtimer-singleshot-2
+ QTimer * timer = %CONVERTTOCPP[QTimer *](pyTimer);
+ timer->setSingleShot(true);
+
+ if (PyObject_TypeCheck(%2, PySideSignalInstance_TypeF())) {
+ PySideSignalInstance *signalInstance = reinterpret_cast<PySideSignalInstance *>(%2);
+ Shiboken::AutoDecRef signalSignature(Shiboken::String::fromFormat("2%s", PySide::Signal::getSignature(signalInstance)));
+ Shiboken::AutoDecRef result(
+ PyObject_CallMethod(pyTimer, "connect", "OsOO",
+ pyTimer,
+ SIGNAL(timeout()),
+ PySide::Signal::getObject(signalInstance),
+ signalSignature.object())
+ );
+ } else {
+ Shiboken::AutoDecRef result(
+ PyObject_CallMethod(pyTimer, "connect", "OsO",
+ pyTimer,
+ SIGNAL(timeout()),
+ %PYARG_2)
+ );
+ }
+
+ timer->connect(timer, &QTimer::timeout, timer, &QObject::deleteLater, Qt::DirectConnection);
+ Shiboken::Object::releaseOwnership(reinterpret_cast<SbkObject *>(pyTimer));
+ Py_XDECREF(pyTimer);
+ timer->start(msec);
+}
+// @snippet qtimer-singleshot-functor
// @snippet qtimer-singleshot-functor-context
auto msec = %1;
} else {
Shiboken::AutoDecRef emptyTuple(PyTuple_New(0));
auto *timerType = Shiboken::SbkType<QTimer>();
- auto newFunc = timerType->tp_new;
- auto initFunc = timerType->tp_init;
+ auto newFunc = reinterpret_cast<newfunc>(PepType_GetSlot(timerType, Py_tp_new));
+ auto initFunc = reinterpret_cast<initproc>(PepType_GetSlot(timerType, Py_tp_init));
auto *pyTimer = newFunc(Shiboken::SbkType<QTimer>(), emptyTuple, nullptr);
initFunc(pyTimer, emptyTuple, nullptr);
// @snippet conversion-sbkobject
// a class supported by QVariant?
const QMetaType metaType = QVariant_resolveMetaType(Py_TYPE(%in));
+bool ok = false;
if (metaType.isValid()) {
QVariant var(metaType);
- Shiboken::Conversions::SpecificConverter converter(metaType.name());
- converter.toCpp(pyIn, var.data());
- %out = var;
-} else {
- // If the type was not encountered, return a default PyObjectWrapper
- %out = QVariant::fromValue(PySide::PyObjectWrapper(%in));
+ auto converterO = converterForQtType(metaType.name());
+ ok = converterO.has_value();
+ if (ok) {
+ converterO.value().toCpp(pyIn, var.data());
+ %out = var;
+ } else {
+ qWarning("%s: Cannot find a converter for \"%s\".",
+ __FUNCTION__, metaType.name());
+ }
}
+
+// If the type was not encountered, return a default PyObjectWrapper
+if (!ok)
+ %out = QVariant::fromValue(PySide::PyObjectWrapper(%in));
// @snippet conversion-sbkobject
// @snippet conversion-pydict
return PySide::qStringToPyUnicode(%in);
// @snippet return-pyunicode
+// @snippet return-pyunicode-from-qlatin1string
+#ifdef Py_LIMITED_API
+return PySide::qStringToPyUnicode(QString::fromLatin1(%in));
+#else
+return PyUnicode_FromKindAndData(PyUnicode_1BYTE_KIND, %in.constData(), %in.size());
+#endif
+// @snippet return-pyunicode-from-qlatin1string
+
+// @snippet qlatin1string-check
+static bool qLatin1StringCheck(PyObject *o)
+{
+ return PyUnicode_CheckExact(o) != 0
+ && _PepUnicode_KIND(o) == PepUnicode_1BYTE_KIND;
+}
+// @snippet qlatin1string-check
+
+// @snippet conversion-pystring-qlatin1string
+const char *data = reinterpret_cast<const char *>(_PepUnicode_DATA(%in));
+const Py_ssize_t len = PyUnicode_GetLength(%in);
+%out = QLatin1String(data, len);
+// @snippet conversion-pystring-qlatin1string
+
// @snippet return-pyunicode-from-qanystringview
return PySide::qStringToPyUnicode(%in.toString());
// @snippet return-pyunicode-from-qanystringview
break;
}
-Shiboken::Conversions::SpecificConverter converter(cppInRef.typeName());
-if (converter) {
- void *ptr = cppInRef.data();
- return converter.toPython(ptr);
-}
+auto converterO = converterForQtType(cppInRef.typeName());
+if (converterO.has_value())
+ return converterO.value().toPython(cppInRef.data());
+
PyErr_Format(PyExc_RuntimeError, "Can't find converter for '%s'.", %in.typeName());
return 0;
// @snippet return-qvariant
// @snippet qdatastream-read-bytes
// @snippet qloggingcategory_to_cpp
+// PYSIDE-2404: Usage of the `get()` function not necessary, the type exists.
QLoggingCategory *category{nullptr};
- Shiboken::Conversions::pythonToCppPointer(SbkPySide6_QtCoreTypes[SBK_QLOGGINGCATEGORY_IDX],
+ Shiboken::Conversions::pythonToCppPointer(SbkPySide6_QtCoreTypeStructs[SBK_QLoggingCategory_IDX].type,
pyArgs[0], &(category));
// @snippet qloggingcategory_to_cpp
Shiboken::AutoDecRef locale(PyImport_ImportModule("locale"));
Shiboken::AutoDecRef getLocale(PyObject_GetAttrString(locale, "getlocale"));
Shiboken::AutoDecRef systemLocale(PyObject_CallObject(getLocale, nullptr));
- Shiboken::AutoDecRef localeCode(PyTuple_GetItem(systemLocale, 0));
+ PyObject* localeCode = PyTuple_GetItem(systemLocale, 0);
%RETURN_TYPE %0;
if (localeCode != Py_None) {
QString localeCodeStr = PySide::pyStringToQString(localeCode);
Py_END_ALLOW_THREADS
// @snippet qcoreapplication-requestpermission
+// @snippet qlockfile-getlockinfo
+qint64 pid{};
+QString hostname, appname;
+%CPPSELF.%FUNCTION_NAME(&pid, &hostname, &appname);
+%PYARG_0 = PyTuple_New(3);
+PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[qint64](pid));
+PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[QString](hostname));
+PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[QString](appname));
+// @snippet qlockfile-getlockinfo
+
+// @snippet darwin_permission_plugin
+#ifdef Q_OS_DARWIN
+#include<QtCore/qplugin.h>
+// register the static plugin and setup its metadata
+Q_IMPORT_PLUGIN(QDarwinCameraPermissionPlugin)
+Q_IMPORT_PLUGIN(QDarwinMicrophonePermissionPlugin)
+Q_IMPORT_PLUGIN(QDarwinBluetoothPermissionPlugin)
+Q_IMPORT_PLUGIN(QDarwinContactsPermissionPlugin)
+Q_IMPORT_PLUGIN(QDarwinCalendarPermissionPlugin)
+#endif
+// @snippet darwin_permission_plugin
+
+// @snippet qt-modifier
+PyObject *_inputDict = PyDict_New();
+// Note: The builtins line is no longer needed since Python 3.10. Undocumented!
+PyDict_SetItemString(_inputDict, "__builtins__", PyEval_GetBuiltins());
+PyDict_SetItemString(_inputDict, "QtCore", module);
+PyDict_SetItemString(_inputDict, "Qt", reinterpret_cast<PyObject *>(pyType));
+// Explicitly not dereferencing the result.
+PyRun_String(R"PY(if True:
+ from enum import Flag
+ from textwrap import dedent
+ from warnings import warn
+ # QtCore and Qt come as globals.
+
+ def func_or(self, other):
+ if isinstance(self, Flag) and isinstance(other, Flag):
+ # this is normal or-ing flags together
+ return Qt.KeyboardModifier(self.value | other.value)
+ return QtCore.QKeyCombination(self, other)
+
+ def func_add(self, other):
+ warn(dedent(f"""
+ The "+" operator is deprecated in Qt For Python 6.0 .
+ Please use "|" instead."""), stacklevel=2)
+ return func_or(self, other)
+
+ Qt.KeyboardModifier.__or__ = func_or
+ Qt.KeyboardModifier.__ror__ = func_or
+ Qt.Modifier.__or__ = func_or
+ Qt.Modifier.__ror__ = func_or
+ Qt.KeyboardModifier.__add__ = func_add
+ Qt.KeyboardModifier.__radd__ = func_add
+ Qt.Modifier.__add__ = func_add
+ Qt.Modifier.__radd__ = func_add
+
+)PY", Py_file_input, _inputDict, _inputDict);
+// @snippet qt-modifier
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// @snippet graphs-qsurfacedataproxy-resetarraynp
-auto *data = QtGraphsHelper::surfaceDataFromNp(%1, %2, %3, %4, %5);
+auto data = QtGraphsHelper::surfaceDataFromNp(%1, %2, %3, %4, %5);
// %CPPSELF.%FUNCTION_NAME
%CPPSELF.resetArray(data);
// @snippet graphs-qsurfacedataproxy-resetarraynp
********************************************************************/
// @snippet gui-declarations
+QT_BEGIN_NAMESPACE
void qt_set_sequence_auto_mnemonic(bool);
+QT_END_NAMESPACE
// @snippet gui-declarations
// @snippet qaccessible-pysidefactory
Shiboken::Object::getOwnership(%PYARG_0); // Ensure the guard is removed
// @snippet qguiapplication-setoverridecursor
+// @snippet qguiapplication-nativeInterface
+bool hasNativeApp = false;
+#if QT_CONFIG(xcb)
+if (auto *x11App = %CPPSELF.nativeInterface<QNativeInterface::QX11Application>()) {
+ hasNativeApp = true;
+ %PYARG_0 = %CONVERTTOPYTHON[QNativeInterface::QX11Application*](x11App);
+}
+#endif
+if (!hasNativeApp) {
+ Py_INCREF(Py_None);
+ %PYARG_0 = Py_None;
+}
+// @snippet qguiapplication-nativeInterface
+
// @snippet qscreen-grabWindow
WId id = %1;
%RETURN_TYPE retval = %CPPSELF.%FUNCTION_NAME(id, %2, %3, %4, %5);
%PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](retval);
// @snippet qscreen-grabWindow
+// @snippet qscreen-nativeInterface
+bool hasNativeScreen = false;
+#ifdef Q_OS_WIN
+if (auto *winScreen = %CPPSELF.nativeInterface<QNativeInterface::QWindowsScreen>()) {
+ hasNativeScreen = true;
+ %PYARG_0 = %CONVERTTOPYTHON[QNativeInterface::QWindowsScreen*](winScreen);
+}
+#endif
+if (!hasNativeScreen) {
+ Py_INCREF(Py_None);
+ %PYARG_0 = Py_None;
+}
+// @snippet qscreen-nativeInterface
+
+// @snippet qx11application-resource-ptr
+ auto *resource = %CPPSELF.%FUNCTION_NAME();
+%PYARG_0 = PyLong_FromVoidPtr(resource);
+// @snippet qx11application-resource-ptr
+
// @snippet qwindow-fromWinId
WId id = %1;
%RETURN_TYPE retval = %CPPSELF.%FUNCTION_NAME(id);
%PYARG_0 = %CONVERTTOPYTHON[int](cppResult);
// @snippet qdrag-exec-arg2
+// @snippet qquaternion-getaxisandangle-vector3d-float
+QVector3D outVec{};
+float angle{};
+%CPPSELF.%FUNCTION_NAME(&outVec, &angle);
+%PYARG_0 = PyTuple_New(2);
+PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[QVector3D](outVec));
+PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[float](angle));
+// @snippet qquaternion-getaxisandangle-vector3d-float
+
+// @snippet qquaternion-geteulerangles
+float pitch{}, yaw{}, roll{};
+%CPPSELF.%FUNCTION_NAME(&pitch, &yaw, &roll);
+%PYARG_0 = PyTuple_New(3);
+PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[float](pitch));
+PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[float](yaw));
+PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[float](roll));
+// @snippet qquaternion-geteulerangles
+
// @snippet qregion-len
return %CPPSELF.rectCount();
// @snippet qregion-len
// @snippet qregion-getitem
-if (_i < 0 || _i >= %CPPSELF.rectCount()) {
- PyErr_SetString(PyExc_IndexError, "index out of bounds");
- return nullptr;
-}
+if (_i < 0 || _i >= %CPPSELF.rectCount())
+ return PyErr_Format(PyExc_IndexError, "index out of bounds");
const QRect cppResult = *(%CPPSELF.cbegin() + _i);
return %CONVERTTOPYTHON[QRect](cppResult);
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// @snippet qvideoframe-bits
+#include "object.h"
%BEGIN_ALLOW_THREADS
%RETURN_TYPE %0 = %CPPSELF.%FUNCTION_NAME(%1);
%END_ALLOW_THREADS
const auto size = %CPPSELF.byteCount();
%PYARG_0 = Shiboken::Buffer::newObject(data, size);
// @snippet qaudiobuffer-const-data
+
+// @snippet qtaudio-namespace-compatibility-alias
+Py_INCREF(pyType);
+PyModule_AddObject(module, "QtAudio", reinterpret_cast<PyObject *>(pyType));
+// @snippet qtaudio-namespace-compatibility-alias
PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[quint16](port));
// @snippet qudpsocket-readdatagram
-// @snippet qhostinfo-lookuphost-callable
-auto *callable = %PYARG_2;
-auto cppCallback = [callable](const QHostInfo &hostInfo)
+// @snippet qhostinfo-lookuphost-functor
+struct QHostInfoFunctor : public Shiboken::PyObjectHolder
+{
+public:
+ using Shiboken::PyObjectHolder::PyObjectHolder;
+
+ void operator()(const QHostInfo &hostInfo);
+};
+
+void QHostInfoFunctor::operator()(const QHostInfo &hostInfo)
{
Shiboken::GilState state;
Shiboken::AutoDecRef arglist(PyTuple_New(1));
auto *pyHostInfo = %CONVERTTOPYTHON[QHostInfo](hostInfo);
PyTuple_SET_ITEM(arglist.object(), 0, pyHostInfo);
- Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
- Py_DECREF(callable);
-};
+ Shiboken::AutoDecRef ret(PyObject_CallObject(object(), arglist));
+ release(); // single shot
+}
+// @snippet qhostinfo-lookuphost-functor
-Py_INCREF(callable);
-%CPPSELF.%FUNCTION_NAME(%1, cppCallback);
+// @snippet qhostinfo-lookuphost-callable
+%CPPSELF.%FUNCTION_NAME(%1, QHostInfoFunctor(%PYARG_2));
// @snippet qhostinfo-lookuphost-callable
// @snippet qipv6address-len
%CPPSELF.c[_i] = item;
return 0;
// @snippet qipv6address-setitem
+
+// @snippet qrestaccessmanager-functor
+class QRestFunctor
+{
+public:
+ explicit QRestFunctor(PyObject *callable) noexcept : m_callable(callable)
+ {
+ Py_INCREF(callable);
+ }
+
+ void operator()(QRestReply &restReply);
+
+private:
+ PyObject *m_callable;
+};
+
+void QRestFunctor::operator()(QRestReply &restReply)
+{
+ Q_ASSERT(m_callable);
+ Shiboken::GilState state;
+ Shiboken::AutoDecRef arglist(PyTuple_New(1));
+ auto *restReplyPtr = &restReply;
+ auto *pyRestReply = %CONVERTTOPYTHON[QRestReply*](restReplyPtr);
+ PyTuple_SET_ITEM(arglist.object(), 0, pyRestReply);
+ Shiboken::AutoDecRef ret(PyObject_CallObject(m_callable, arglist));
+ Py_DECREF(m_callable);
+ m_callable = nullptr;
+}
+// @snippet qrestaccessmanager-functor
+
+// @snippet qrestaccessmanager-callback
+auto *networkReply = %CPPSELF.%FUNCTION_NAME(%1, %2, QRestFunctor(%PYARG_3));
+%PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](networkReply);
+// @snippet qrestaccessmanager-callback
+
+// @snippet qrestaccessmanager-data-callback
+auto *networkReply = %CPPSELF.%FUNCTION_NAME(%1, %2, %3, QRestFunctor(%PYARG_4));
+%PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](networkReply);
+// @snippet qrestaccessmanager-data-callback
+
+// @snippet qrestaccessmanager-method-data-callback
+auto *networkReply = %CPPSELF.%FUNCTION_NAME(%1, %2, %3, %4, QRestFunctor(%PYARG_5));
+%PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](networkReply);
+// @snippet qrestaccessmanager-method-data-callback
+
+// @snippet qrestreply-readjson
+QJsonParseError jsonParseError;
+std::optional<QJsonDocument> documentOptional = %CPPSELF.%FUNCTION_NAME(&jsonParseError);
+
+PyObject *pyDocument{};
+if (documentOptional.has_value()) {
+ const auto &document = documentOptional.value();
+ pyDocument = %CONVERTTOPYTHON[QJsonDocument](document);
+} else {
+ pyDocument = Py_None;
+ Py_INCREF(Py_None);
+}
+
+%PYARG_0 = PyTuple_New(2);
+PyTuple_SetItem(%PYARG_0, 0, pyDocument);
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[QJsonParseError](jsonParseError));
+// @snippet qrestreply-readjson
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-// @snippet qabstractoauth-setmodifyparametersfunction
-auto callable = %PYARG_1;
-auto callback = [callable](QAbstractOAuth::Stage stage, QMultiMap<QString, QVariant>* dictPointer) -> void
+// @snippet qabstractoauth-lookuphost-functor
+struct QAbstractOAuthModifyFunctor : public Shiboken::PyObjectHolder
+{
+public:
+ using Shiboken::PyObjectHolder::PyObjectHolder;
+
+ void operator()(QAbstractOAuth::Stage stage, QMultiMap<QString, QVariant>* dictPointer);
+};
+
+void QAbstractOAuthModifyFunctor::operator()(QAbstractOAuth::Stage stage,
+ QMultiMap<QString, QVariant>* dictPointer)
{
+ auto *callable = object();
if (!PyCallable_Check(callable)) {
- qWarning("Argument 1 of %FUNCTION_NAME must be a callable.");
+ qWarning("Argument 1 of setModifyParametersFunction() must be a callable.");
return;
}
Shiboken::GilState state;
dictPointer->replace(cppKey, cppValue);
}
}
+}
+// @snippet qabstractoauth-lookuphost-functor
- Py_DECREF(callable);
- return;
-
-};
-Py_INCREF(callable);
-%CPPSELF.%FUNCTION_NAME(callback);
+// @snippet qabstractoauth-setmodifyparametersfunction
+%CPPSELF.%FUNCTION_NAME(QAbstractOAuthModifyFunctor(%PYARG_1));
// @snippet qabstractoauth-setmodifyparametersfunction
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+/*********************************************************************
+ * INJECT CODE
+ ********************************************************************/
+
+// @snippet darwin_location_permission_plugin
+#ifdef Q_OS_DARWIN
+#include<QtCore/qplugin.h>
+// register the static plugin and setup its metadata
+Q_IMPORT_PLUGIN(QDarwinLocationPermissionPlugin)
+#endif
+// @snippet darwin_location_permission_plugin
// @snippet qmlsingleton
%PYARG_0 = PySide::Qml::qmlSingletonMacro(%ARGUMENT_NAMES);
// @snippet qmlsingleton
+
+// @snippet qqmlengine-singletoninstance-qmltypeid
+QJSValue instance = %CPPSELF.singletonInstance<QJSValue>(%1);
+if (instance.isNull()) {
+ Py_INCREF(Py_None);
+ %PYARG_0 = Py_None;
+} else if (instance.isQObject()) {
+ QObject *result = instance.toQObject();
+ %PYARG_0 = %CONVERTTOPYTHON[QObject *](result);
+} else {
+ %PYARG_0 = %CONVERTTOPYTHON[QJSValue](instance);
+}
+// @snippet qqmlengine-singletoninstance-qmltypeid
+
+// @snippet qqmlengine-singletoninstance-typename
+QJSValue instance = %CPPSELF.singletonInstance<QJSValue>(%1, %2);
+if (instance.isNull()) {
+ Py_INCREF(Py_None);
+ %PYARG_0 = Py_None;
+} else if (instance.isQObject()) {
+ QObject *result = instance.toQObject();
+ %PYARG_0 = %CONVERTTOPYTHON[QObject *](result);
+} else {
+ %PYARG_0 = %CONVERTTOPYTHON[QJSValue](instance);
+}
+// @snippet qqmlengine-singletoninstance-typename
--- /dev/null
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+/*********************************************************************
+ * INJECT CODE
+ ********************************************************************/
+
+// @snippet call-quick-test-main
+static int callQuickTestMain(const QString &name, QObject *setup,
+ QStringList argv, QString dir)
+{
+ if (dir.isEmpty())
+ dir = QDir::currentPath();
+ if (argv.isEmpty())
+ argv.append(name);
+
+ std::vector<QByteArray> argvB;
+ std::vector<char *> argvC;
+ const auto argc = argv.size();
+ argvB.reserve(argc);
+ argvC.reserve(argc);
+ for (const auto &arg : argv) {
+ argvB.emplace_back(arg.toUtf8());
+ argvC.push_back(argvB.back().data());
+ }
+
+ return quick_test_main_with_setup(int(argc), argvC.data(),
+ name.toUtf8().constData(),
+ dir.toUtf8().constData(), setup);
+}
+// @snippet call-quick-test-main
+
+// @snippet quick-test-main
+const int exitCode = callQuickTestMain(%1, nullptr, %2, %3);
+%PYARG_0 = %CONVERTTOPYTHON[int](exitCode);
+// @snippet quick-test-main
+
+// @snippet quick-test-main_with_setup
+Shiboken::AutoDecRef pySetupObject(PyObject_CallObject(reinterpret_cast<PyObject *>(%2), nullptr));
+if (pySetupObject.isNull() || PyErr_Occurred() != nullptr)
+ return nullptr;
+
+/// Convenience to convert a PyObject to QObject
+QObject *setupObject = PySide::convertToQObject(pySetupObject.object(), true /* raiseError */);
+if (setupObject == nullptr)
+ return nullptr;
+
+const int exitCode = callQuickTestMain(%1, setupObject, %3, %4);
+%PYARG_0 = %CONVERTTOPYTHON[int](exitCode);
+// @snippet quick-test-main_with_setup
}
if (!PyErr_Occurred())
- PyErr_SetString(PyExc_RuntimeError, "Unable to open/read ui device");
+ PyErr_Format(PyExc_RuntimeError, "Unable to open/read ui device");
return nullptr;
}
QByteArray uiFileName(arg1);
Py_DECREF(strObj);
-QFile uiFile(QString::fromUtf8(uiFileName));
-
-if (!uiFile.exists()) {
- qCritical().noquote() << "File" << uiFileName << "does not exists";
+if (uiFileName.isEmpty()) {
+ qCritical() << "Error converting the UI filename to QByteArray";
Py_RETURN_NONE;
}
-if (uiFileName.isEmpty()) {
- qCritical() << "Error converting the UI filename to QByteArray";
+QFile uiFile(QString::fromUtf8(uiFileName));
+
+if (!uiFile.exists()) {
+ qCritical().noquote() << "File" << uiFileName << "does not exist";
Py_RETURN_NONE;
}
QProcess uicProcess;
uicProcess.start(uicBin, uicArgs);
-if (!uicProcess.waitForFinished()) {
- qCritical() << "Cannot run 'pyside6-uic': " << uicProcess.errorString() << " - "
- << "Exit status " << uicProcess.exitStatus()
- << " (" << uicProcess.exitCode() << ")\n"
- << "Check if 'pyside6-uic' is in PATH";
+if (!uicProcess.waitForStarted()) {
+ qCritical().noquote() << "Cannot run '" << uicBin << "': "
+ << uicProcess.errorString() << " - Check if 'pyside6-uic' is in PATH";
Py_RETURN_NONE;
}
+
+if (!uicProcess.waitForFinished()
+ || uicProcess.exitStatus() != QProcess::NormalExit
+ || uicProcess.exitCode() != 0) {
+ qCritical().noquote() << '\'' << uicBin << "' failed: "
+ << uicProcess.errorString() << " - Exit status " << uicProcess.exitStatus()
+ << " (" << uicProcess.exitCode() << ")\n";
+ Py_RETURN_NONE;
+}
+
QByteArray uiFileContent = uicProcess.readAllStandardOutput();
QByteArray errorOutput = uicProcess.readAllStandardError();
if (!errorOutput.isEmpty()) {
- qCritical().noquote() << errorOutput;
+ qCritical().noquote() << '\'' << uicBin << "' failed: " << errorOutput;
Py_RETURN_NONE;
}
while (!reader.atEnd() && baseClassName.isEmpty() && className.isEmpty()) {
auto token = reader.readNext();
if (token == QXmlStreamReader::StartElement && reader.name() == u"widget") {
- baseClassName = reader.attributes().value(QLatin1String("class")).toUtf8();
- className = reader.attributes().value(QLatin1String("name")).toUtf8();
+ baseClassName = reader.attributes().value(QLatin1StringView("class")).toUtf8();
+ className = reader.attributes().value(QLatin1StringView("name")).toUtf8();
}
}
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-// @snippet qwebenginecookiestore-setcookiefilter
-auto callable = %PYARG_1;
-auto callback = [callable](const QWebEngineCookieStore::FilterRequest& filterRequest) -> bool
+// @snippet qwebenginecookiestore-functor
+struct QWebEngineCookieFilterFunctor : public Shiboken::PyObjectHolder
+{
+ using Shiboken::PyObjectHolder::PyObjectHolder;
+
+ bool operator()(const QWebEngineCookieStore::FilterRequest& filterRequest) const;
+};
+
+bool QWebEngineCookieFilterFunctor::operator()(const QWebEngineCookieStore::FilterRequest &
+ filterRequest) const
{
Shiboken::GilState state;
Shiboken::AutoDecRef arglist(PyTuple_New(1));
PyTuple_SET_ITEM(arglist, 0,
- %CONVERTTOPYTHON[QWebEngineCookieStore::FilterRequest](filterRequest));
- Py_INCREF(callable);
- Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
- Py_DECREF(callable);
+ %CONVERTTOPYTHON[QWebEngineCookieStore::FilterRequest](filterRequest));
+ Shiboken::AutoDecRef ret(PyObject_CallObject(object(), arglist));
return ret.object() == Py_True;
-};
+}
+// @snippet qwebenginecookiestore-functor
-%CPPSELF.%FUNCTION_NAME(callback);
+// @snippet qwebenginecookiestore-setcookiefilter
+%CPPSELF.%FUNCTION_NAME(QWebEngineCookieFilterFunctor(%PYARG_1));
// @snippet qwebenginecookiestore-setcookiefilter
-// @snippet qwebengineprofile-setnotificationpresenter
-auto callable = %PYARG_1;
-auto callback = [callable](std::unique_ptr<QWebEngineNotification> webEngineNotification) -> void
+// @snippet qwebengineprofile-functor
+struct QWebEngineNotificationFunctor : public Shiboken::PyObjectHolder
+{
+ using Shiboken::PyObjectHolder::PyObjectHolder;
+
+ void operator()(std::unique_ptr<QWebEngineNotification> webEngineNotification);
+};
+
+void QWebEngineNotificationFunctor::operator()
+ (std::unique_ptr<QWebEngineNotification> webEngineNotification)
{
Shiboken::GilState state;
Shiboken::AutoDecRef arglist(PyTuple_New(1));
auto *notification = webEngineNotification.release();
PyTuple_SET_ITEM(arglist.object(), 0,
%CONVERTTOPYTHON[QWebEngineNotification*](notification));
- Py_INCREF(callable);
- Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
- Py_DECREF(callable);
+ Shiboken::AutoDecRef ret(PyObject_CallObject(object(), arglist));
};
+// @snippet qwebengineprofile-functor
-%CPPSELF.%FUNCTION_NAME(callback);
// @snippet qwebengineprofile-setnotificationpresenter
+%CPPSELF.%FUNCTION_NAME(QWebEngineNotificationFunctor(%PYARG_1));
+// @snippet qwebengineprofile-setnotificationpresenter
+
+// @snippet qwebenginepage-javascriptprompt-virtual-redirect
+std::pair<bool, QString> resultPair = javaScriptPromptPyOverride(gil, pyOverride.object(), securityOrigin, msg, defaultValue);
+result->assign(resultPair.second);
+return resultPair.first;
+// @snippet qwebenginepage-javascriptprompt-virtual-redirect
+
+// @snippet qwebenginepage-javascriptprompt-return
+QString str;
+%RETURN_TYPE retval_ = %CPPSELF.%FUNCTION_NAME(%1, %2, %3, &str);
+%PYARG_0 = PyTuple_New(2);
+PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
+PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[QString](str));
+// @snippet qwebenginepage-javascriptprompt-return
#ifndef _QLAYOUT_HELP_FUNCTIONS_
#define _QLAYOUT_HELP_FUNCTIONS_ // Guard for jumbo builds
+static const char msgInvalidParameterAdd[] =
+ "Invalid parameter None passed to addLayoutOwnership().";
+static const char msgInvalidParameterRemoval[] =
+ "Invalid parameter None passed to removeLayoutOwnership().";
+
void addLayoutOwnership(QLayout *layout, QLayoutItem *item);
void removeLayoutOwnership(QLayout *layout, QWidget *widget);
inline void addLayoutOwnership(QLayout *layout, QWidget *widget)
{
+ if (layout == nullptr || widget == nullptr) {
+ PyErr_SetString(PyExc_RuntimeError, msgInvalidParameterAdd);
+ return;
+ }
+
//transfer ownership to parent widget
QWidget *lw = layout->parentWidget();
QWidget *pw = widget->parentWidget();
inline void addLayoutOwnership(QLayout *layout, QLayout *other)
{
+ if (layout == nullptr || other == nullptr) {
+ PyErr_SetString(PyExc_RuntimeError, msgInvalidParameterAdd);
+ return;
+ }
+
//transfer all children widgets from other to layout parent widget
QWidget *parent = layout->parentWidget();
if (!parent) {
inline void addLayoutOwnership(QLayout *layout, QLayoutItem *item)
{
- if (!item)
+
+ if (layout == nullptr || item == nullptr) {
+ PyErr_SetString(PyExc_RuntimeError, msgInvalidParameterAdd);
return;
+ }
if (QWidget *w = item->widget()) {
addLayoutOwnership(layout, w);
static void removeWidgetFromLayout(QLayout *layout, QWidget *widget)
{
+ if (layout == nullptr || widget == nullptr) {
+ PyErr_SetString(PyExc_RuntimeError, msgInvalidParameterRemoval);
+ return;
+ }
+
if (QWidget *parent = widget->parentWidget()) {
//give the ownership to parent
Shiboken::AutoDecRef pyParent(%CONVERTTOPYTHON[QWidget *](parent));
inline void removeLayoutOwnership(QLayout *layout, QLayoutItem *item)
{
-
- if (item == nullptr) {
- PyErr_Format(PyExc_RuntimeError, "Item for removal from layout is None, or invalid.");
+ if (layout == nullptr || item == nullptr) {
+ PyErr_SetString(PyExc_RuntimeError, msgInvalidParameterRemoval);
return;
}
inline void removeLayoutOwnership(QLayout *layout, QWidget *widget)
{
- if (!widget)
+ if (layout == nullptr || widget == nullptr) {
+ PyErr_SetString(PyExc_RuntimeError, msgInvalidParameterRemoval);
return;
+ }
for (int i = 0, i_max = layout->count(); i < i_max; ++i) {
QLayoutItem *item = layout->itemAt(i);
%CPPSELF.setAlignment(%1);
// @snippet qlayout-setalignment
-// @snippet addownership-0
-addLayoutOwnership(%CPPSELF, %0);
-// @snippet addownership-0
+// @snippet addownership-item-at
+if (%0 != nullptr)
+ addLayoutOwnership(%CPPSELF, %0);
+// @snippet addownership-item-at
// @snippet addownership-1
addLayoutOwnership(%CPPSELF, %1);
// @snippet qwidget-style
QStyle *myStyle = %CPPSELF->style();
if (myStyle && qApp) {
-%PYARG_0 = %CONVERTTOPYTHON[QStyle *](myStyle);
+ bool keepReference = true;
+ %PYARG_0 = %CONVERTTOPYTHON[QStyle *](myStyle);
QStyle *appStyle = qApp->style();
if (appStyle == myStyle) {
Shiboken::AutoDecRef pyApp(%CONVERTTOPYTHON[QApplication *](qApp));
- Shiboken::Object::setParent(pyApp, %PYARG_0);
- Shiboken::Object::releaseOwnership(%PYARG_0);
- } else {
- Shiboken::Object::keepReference(reinterpret_cast<SbkObject *>(%PYSELF), "__style__", %PYARG_0);
+ // Do not set parentship when qApp is embedded
+ if (Shiboken::Object::wasCreatedByPython(reinterpret_cast<SbkObject *>(pyApp.object()))) {
+ Shiboken::Object::setParent(pyApp, %PYARG_0);
+ Shiboken::Object::releaseOwnership(%PYARG_0);
+ keepReference = false;
+ }
}
+ if (keepReference)
+ Shiboken::Object::keepReference(reinterpret_cast<SbkObject *>(%PYSELF), "__style__", %PYARG_0);
}
// @snippet qwidget-style
case QStyleOption::SO_Default:
break;
case QStyleOption::SO_FocusRect:
- return "StyleOptionFocusRect";
+ return "QStyleOptionFocusRect";
case QStyleOption::SO_Button:
- return "StyleOptionButton";
+ return "QStyleOptionButton";
case QStyleOption::SO_Tab:
- return "StyleOptionTab";
+ return "QStyleOptionTab";
case QStyleOption::SO_MenuItem:
- return "StyleOptionMenuItem";
+ return "QStyleOptionMenuItem";
case QStyleOption::SO_Frame:
- return "StyleOptionFrame";
+ return "QStyleOptionFrame";
case QStyleOption::SO_ProgressBar:
- return "StyleOptionProgressBar";
+ return "QStyleOptionProgressBar";
case QStyleOption::SO_ToolBox:
- return "StyleOptionToolBox";
+ return "QStyleOptionToolBox";
case QStyleOption::SO_Header:
- return "StyleOptionHeader";
+ return "QStyleOptionHeader";
case QStyleOption::SO_DockWidget:
- return "StyleOptionDockWidget";
+ return "QStyleOptionDockWidget";
case QStyleOption::SO_ViewItem:
- return "StyleOptionViewItem";
+ return "QStyleOptionViewItem";
case QStyleOption::SO_TabWidgetFrame:
- return "StyleOptionTabWidgetFrame";
+ return "QStyleOptionTabWidgetFrame";
case QStyleOption::SO_TabBarBase:
- return "StyleOptionTabBarBase";
+ return "QStyleOptionTabBarBase";
case QStyleOption::SO_RubberBand:
- return "StyleOptionRubberBand";
+ return "QStyleOptionRubberBand";
case QStyleOption::SO_ToolBar:
- return "StyleOptionToolBar";
+ return "QStyleOptionToolBar";
case QStyleOption::SO_GraphicsItem:
- return "StyleOptionGraphicsItem";
+ return "QStyleOptionGraphicsItem";
case QStyleOption::SO_Slider:
- return "StyleOptionSlider";
+ return "QStyleOptionSlider";
case QStyleOption::SO_SpinBox:
- return "StyleOptionSpinBox";
+ return "QStyleOptionSpinBox";
case QStyleOption::SO_ToolButton:
- return "StyleOptionToolButton";
+ return "QStyleOptionToolButton";
case QStyleOption::SO_ComboBox:
- return "StyleOptionComboBox";
+ return "QStyleOptionComboBox";
case QStyleOption::SO_TitleBar:
- return "StyleOptionTitleBar";
+ return "QStyleOptionTitleBar";
case QStyleOption::SO_GroupBox:
- return "StyleOptionGroupBox";
+ return "QStyleOptionGroupBox";
case QStyleOption::SO_SizeGrip:
- return "StyleOptionSizeGrip";
+ return "QStyleOptionSizeGrip";
default:
break;
}
// @snippet qwizardpage-registerfield
auto *signalInst = reinterpret_cast<PySideSignalInstance *>(%PYARG_4);
const auto data = PySide::Signal::getEmitterData(signalInst);
-if (data.methodIndex == -1) {
- PyErr_SetString(PyExc_RuntimeError, "QWizardPage::registerField(): Unable to retrieve signal emitter.");
- return nullptr;
-}
+if (data.methodIndex == -1)
+ return PyErr_Format(PyExc_RuntimeError, "QWizardPage::registerField(): Unable to retrieve signal emitter.");
const auto method = data.emitter->metaObject()->method(data.methodIndex);
const QByteArray signature = QByteArrayLiteral("2") + method.methodSignature();
%BEGIN_ALLOW_THREADS
%END_ALLOW_THREADS
// @snippet qwizardpage-registerfield
+// The constructor heuristics generate setting a parent-child relationship
+// when creating a QDialog with parent. This causes the dialog to leak
+// when it synchronous exec() is used instead of asynchronous show().
+// In that case, remove the parent-child relationship.
+// @snippet qdialog-exec-remove-parent-relation
+Shiboken::Object::removeParent(reinterpret_cast<SbkObject *>(%PYSELF));
+// @snippet qdialog-exec-remove-parent-relation
+
/*********************************************************************
* CONVERSIONS
********************************************************************/
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QIOPIPE_H
+#define QIOPIPE_H
+
+#include <QtCore/qiodevicebase.h>
+#include <QtCore/qobject.h>
+
+QT_BEGIN_NAMESPACE
+
+class QIODevice;
+
+namespace QtCoreHelper
+{
+
+class QIOPipePrivate;
+class QIOPipe : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QIOPipe)
+
+public:
+ QIOPipe(QObject *parent = nullptr);
+
+ bool open(QIODeviceBase::OpenMode mode);
+
+ QIODevice *end1() const;
+ QIODevice *end2() const;
+};
+
+} // namespace QtCoreHelper
+
+QT_END_NAMESPACE
+
+#endif // QIOPIPE_H
Q_DECLARE_INTERFACE(QDesignerCustomWidgetCollectionInterface, "org.qt-project.Qt.QDesignerCustomWidgetCollectionInterface")
#endif
+struct _object; // PyObject
+
+QT_BEGIN_NAMESPACE
+
// Extension implementations need to inherit QObject which cannot be done in Python.
// Provide a base class (cf QPyTextObject).
explicit QPyDesignerTaskMenuExtension(QObject *parent = nullptr) : QObject(parent) {}
};
-struct _object; // PyObject
-
class QPyDesignerCustomWidgetCollection : public QDesignerCustomWidgetCollectionInterface
{
public:
QList<QDesignerCustomWidgetInterface *> m_customWidgets;
};
+QT_END_NAMESPACE
+
#endif // QPYDESIGNEREXTENSIONS_H
Q_DECLARE_INTERFACE(QQmlParserStatus, "org.qt-project.Qt.QQmlParserStatus")
#endif
+QT_BEGIN_NAMESPACE
+
// Inherit from QObject such that QQmlParserStatus can be found at
// a fixed offset (RegisterType::parserStatusCast).
class QPyQmlParserStatus : public QObject, public QQmlParserStatus
explicit QPyQmlParserStatus(QObject *parent = nullptr) : QObject(parent) {}
};
+QT_END_NAMESPACE
+
#endif // QPYQMLPARSERSTATUS_H
Q_DECLARE_INTERFACE(QQmlPropertyValueSource, "org.qt-project.Qt.QQmlPropertyValueSource")
#endif
+QT_BEGIN_NAMESPACE
+
// Inherit from QObject such that QQmlPropertyValueSource can be found at
// a fixed offset (RegisterType::valueSourceCast).
class QPyQmlPropertyValueSource : public QObject, public QQmlPropertyValueSource
explicit QPyQmlPropertyValueSource(QObject *parent = nullptr) : QObject(parent) {}
};
+QT_END_NAMESPACE
+
#endif // QPYQMLPROPERTYVALUESOURCE_H
Q_DECLARE_INTERFACE(QTextObjectInterface, "org.qt-project.Qt.QTextObjectInterface")
#endif
+QT_BEGIN_NAMESPACE
class QPyTextObject : public QObject, public QTextObjectInterface
{
Q_OBJECT
public:
QPyTextObject(QObject *parent = nullptr) : QObject(parent) {}
};
+QT_END_NAMESPACE
+
#endif
#include <memory>
+QT_BEGIN_NAMESPACE
+
namespace QtCoreHelper {
using MutexLocker = QT_PREPEND_NAMESPACE(QMutexLocker<QMutex>);
} // namespace QtCoreHelper
+QT_END_NAMESPACE
+
#endif // QTCOREHELPER_H
#include <sbkpython.h>
-#include <QtDataVisualization/QSurfaceDataProxy>
-#include <QtCore/QList>
+#include <QtDataVisualization/qsurfacedataproxy.h>
+#include <QtCore/qlist.h>
namespace QtDataVisualizationHelper {
#include <QtDBus/qdbuspendingcall.h>
#include <QtDBus/qdbusreply.h>
+QT_BEGIN_NAMESPACE
namespace QtDBusHelper {
// A Python-bindings friendly, non-template QDBusReply
} // namespace QtDBusHelper
+QT_END_NAMESPACE
+
#endif // QTDBUSHELPER_H
#include <sbkpython.h>
-#include <QtGraphs/QSurfaceDataProxy>
-#include <QtCore/QList>
+#include <QtGraphs/qsurfacedataproxy.h>
+#include <QtCore/qlist.h>
namespace QtGraphsHelper {
-QSurfaceDataArray *surfaceDataFromNp(double x, double deltaX, double z, double deltaZ,
- PyObject *data);
+QSurfaceDataArray surfaceDataFromNp(double x, double deltaX, double z, double deltaZ,
+ PyObject *data);
} // namespace QtGraphsHelper
#include <QtGui/QGuiApplication>
+QT_BEGIN_NAMESPACE
namespace QtGuiHelper {
class QOverrideCursorGuard
};
} // namespace QtGuiHelper
+QT_END_NAMESPACE
#endif // QTGUIHELPER_H
Note that this fixing code is run after all initializations, but before the
import is finished. But that is no problem since the module is passed in.
-
-PYSIDE-1735: This is also used now for missing other functions (overwriting __or__
- in Qt.(Keyboard)Modifier).
"""
-import warnings
-from textwrap import dedent
-
-
-class PySideDeprecationWarningRemovedInQt6(Warning):
- pass
-
-
-def constData(self):
- cls = self.__class__
- name = cls.__qualname__
- warnings.warn(dedent(f"""
- {name}.constData is unpythonic and will be removed in Qt For Python 6.0 .
- Please use {name}.data instead."""), PySideDeprecationWarningRemovedInQt6, stacklevel=2)
- return cls.data(self)
-
-
-# No longer needed but kept for reference.
-def _unused_fix_for_QtGui(QtGui):
- for name, cls in QtGui.__dict__.items():
- if name.startswith("QMatrix") and "data" in cls.__dict__:
- cls.constData = constData
-
-# PYSIDE-1735: Fix for a special enum function
-def fix_for_QtCore(QtCore):
- from enum import Flag
- Qt = QtCore.Qt
- flag_or = Flag.__or__
-
- def func_or(self, other):
- if isinstance(self, Flag) and isinstance(other, Flag):
- # this is normal or-ing flags together
- return Qt.KeyboardModifier(self.value | other.value)
- return QtCore.QKeyCombination(self, other)
-
- def func_add(self, other):
- warnings.warn(dedent(f"""
- The "+" operator is deprecated in Qt For Python 6.0 .
- Please use "|" instead."""), PySideDeprecationWarningRemovedInQt6, stacklevel=2)
- return func_or(self, other)
-
- Qt.KeyboardModifier.__or__ = func_or
- Qt.KeyboardModifier.__ror__ = func_or
- Qt.Modifier.__or__ = func_or
- Qt.Modifier.__ror__ = func_or
- Qt.KeyboardModifier.__add__ = func_add
- Qt.KeyboardModifier.__radd__ = func_add
- Qt.Modifier.__add__ = func_add
- Qt.Modifier.__radd__ = func_add
-
# eof
"""
import argparse
-import inspect
+import inspect # noqa: F401
import logging
import os
import sys
-import typing
+import typing # noqa: F401
from pathlib import Path
-from types import SimpleNamespace
+from types import SimpleNamespace # noqa: F401
# Can we use forward references?
USE_PEP563 = sys.version_info[:2] >= (3, 7)
ps = os.pathsep
if options.sys_path:
# make sure to propagate the paths from sys_path to subprocesses
- normpath = lambda x: os.fspath(Path(x).resolve())
+ normpath = lambda x: os.fspath(Path(x).resolve()) # noqa: E731
sys_path = [normpath(_) for _ in options.sys_path]
sys.path[0:0] = sys_path
pypath = ps.join(sys_path)
parser = argparse.ArgumentParser(
description="This script generates the .pyi file for all PySide modules.")
parser.add_argument("modules", nargs="+",
- help="'all' or the names of modules to build (QtCore QtGui etc.)")
+ help="'all' or the names of modules to build (QtCore QtGui etc.)")
parser.add_argument("--quiet", action="store_true", help="Run quietly")
- parser.add_argument("--check", action="store_true", help="Test the output if on Python 3")
parser.add_argument("--outpath",
- help="the output directory (default = binary location)")
+ help="the output directory (default = binary location)")
parser.add_argument("--sys-path", nargs="+",
- help="a list of strings prepended to sys.path")
+ help="a list of strings prepended to sys.path")
parser.add_argument("--feature", nargs="+", choices=["snake_case", "true_property"], default=[],
- help="""a list of feature names. Example: `--feature snake_case true_property`""")
+ help="""a list of feature names. """
+ """Example: `--feature snake_case true_property`. """
+ """Currently not available for PyPy.""")
options = parser.parse_args()
qtest_env = os.environ.get("QTEST_ENVIRONMENT", "")
string(REPLACE "${PATH_SEP}" ";" ${varname} "${ARGN}")
endmacro()
+# Check for presence of QtOpenGL and modify module variables
+# accordingly
+macro(check_qt_opengl module include_var deps_var dropped_entries_var)
+ if (Qt${QT_MAJOR_VERSION}OpenGL_FOUND)
+ message(STATUS "Qt${QT_MAJOR_VERSION}${module}: Building with OpenGL")
+ list(APPEND ${include_var} ${Qt${QT_MAJOR_VERSION}OpenGL_INCLUDE_DIRS}
+ ${QtOpenGL_GEN_DIR})
+ list(APPEND ${deps_var} QtOpenGL)
+ else()
+ message(STATUS "Qt${QT_MAJOR_VERSION}${module}: Dropping OpenGL")
+ # This is a dummy entry creating a conditional typesystem keyword
+ list(APPEND ${dropped_entries_var} "QtOpenGL")
+ endif()
+endmacro()
+
# set size optimization flags for pyside6
macro(append_size_optimization_flags _module_name)
if(NOT QFP_NO_OVERRIDE_OPTIMIZATION_FLAGS)
Quick
Quick3D
QuickControls2
+ QuickTest
QuickWidgets
RemoteObjects
Scxml
return()
endif()
else()
- # We are building the docs as a standalone project, likely via setup.py build_rst_docs
+ # We are building the docs as a standalone project, likely via setup.py build_base_docs
# command. Perform stricter sanity checks.
if(NOT SPHINX_BUILD)
message(FATAL_ERROR "sphinx-build command not found. Please set the SPHINX_BUILD variable.")
endif()
set(DOC_DATA_DIR "${CMAKE_CURRENT_BINARY_DIR}/qdoc-output")
+# Directory for sphinx-generated files to build the HTML website. If changed,
+# update "build_scripts/main.py" in "PysideBaseDocs" class at line:
+# self.sphinx_src = self.out_dir / "base".
+set(DOC_BASE_DIR "base")
set(ENV_INHERITANCE_FILE "${CMAKE_CURRENT_BINARY_DIR}/inheritance.json")
file(REMOVE ${CMAKE_CURRENT_LIST_DIR}/pyside.qdocconf ${CMAKE_CURRENT_LIST_DIR}/pyside.qdocconf.in)
# We need to find the interpreter when running this only
-# for a rst_build_docs case, and not a full doc build
+# for the 'build_base_docs' case, and not a full doc build
if (NOT FULLDOCSBUILD)
find_package(Python COMPONENTS Interpreter)
endif()
if (FULLDOCSBUILD)
# Fetch and transform the snippets from Qt
set(SNIPPETS_TOOL "${TOOLS_DIR}/snippets_translate/main.py")
- set(SNIPPETS_TARGET ${CMAKE_CURRENT_BINARY_DIR}/rst/codesnippets)
+ set(SNIPPETS_TARGET ${CMAKE_CURRENT_BINARY_DIR}/${DOC_BASE_DIR}/codesnippets)
# Note QT_SRC_DIR points to 'qtbase',
# so we use the general SRC directory to copy all the other snippets
endif()
# Generate example gallery
-set(EXAMPLE_TOOL_TARGET "${CMAKE_CURRENT_BINARY_DIR}/rst/examples")
+set(EXAMPLE_TOOL_TARGET "${CMAKE_CURRENT_BINARY_DIR}/${DOC_BASE_DIR}/examples")
set(EXAMPLE_TOOL_OPTIONS --target "${EXAMPLE_TOOL_TARGET}" --qt-src-dir "${QT_SRC_DIR}")
if (QUIET_BUILD)
list(APPEND EXAMPLE_TOOL_OPTIONS "-q")
add_custom_target(apidoc
COMMAND ${CMAKE_COMMAND} -E env INHERITANCE_FILE=${ENV_INHERITANCE_FILE}
${SHIBOKEN_PYTHON_INTERPRETER} ${SPHINX_BUILD} -b ${DOC_OUTPUT_FORMAT}
- -j 6 ${CMAKE_CURRENT_BINARY_DIR}/rst html
+ -j 6 ${CMAKE_CURRENT_BINARY_DIR}/${DOC_BASE_DIR} html
COMMENT "Generating PySide htmls..."
)
endif()
# create conf.py based on conf.py.in
-configure_file("conf.py.in" "rst/conf.py" @ONLY)
+configure_file("conf.py.in" "${DOC_BASE_DIR}/conf.py" @ONLY)
-set(CODE_SNIPPET_ROOT "${CMAKE_CURRENT_BINARY_DIR}/rst/codesnippets")
+set(CODE_SNIPPET_ROOT "${CMAKE_CURRENT_BINARY_DIR}/${DOC_BASE_DIR}/codesnippets")
if (FULLDOCSBUILD)
shiboken_get_tool_shell_wrapper(shiboken tool_wrapper)
-add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/rst/PySide6/QtCore/index.rst"
+add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${DOC_BASE_DIR}/PySide6/QtCore/index.rst"
COMMAND
${tool_wrapper}
$<TARGET_FILE:Shiboken6::shiboken6>
--typesystem-paths="${QDOC_TYPESYSTEM_PATH}"
--library-source-dir=${QT_SRC_DIR}
--documentation-data-dir=${DOC_DATA_DIR}/webxml
- --output-directory=${CMAKE_CURRENT_BINARY_DIR}/rst
+ --output-directory=${CMAKE_CURRENT_BINARY_DIR}/${DOC_BASE_DIR}
--documentation-code-snippets-dir=${CODE_SNIPPET_ROOT}
--snippets-path-rewrite=${QT_ROOT_PATH}:${CODE_SNIPPET_ROOT}
- --documentation-extra-sections-dir=${CMAKE_CURRENT_BINARY_DIR}/rst/extras
- --additional-documentation=${CMAKE_CURRENT_BINARY_DIR}/rst/additionaldocs.lst
+ --documentation-extra-sections-dir=${CMAKE_CURRENT_BINARY_DIR}/${DOC_BASE_DIR}/extras
+ --additional-documentation=${CMAKE_CURRENT_BINARY_DIR}/${DOC_BASE_DIR}/additionaldocs.lst
--inheritance-file=${ENV_INHERITANCE_FILE}
${global_typesystem}
WORKING_DIRECTORY ${${module}_SOURCE_DIR}
COMMENT "Running generator to generate documentation...")
endif()
-add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/rst/extras"
- COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/rst
+add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${DOC_BASE_DIR}/extras"
+ COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/${DOC_BASE_DIR}
COMMENT "Copying docs...")
add_custom_target("doc_copy"
- DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/rst/extras")
+ DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${DOC_BASE_DIR}/extras")
add_custom_target("docrsts"
- DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/rst/PySide6/QtCore/index.rst")
+ DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${DOC_BASE_DIR}/PySide6/QtCore/index.rst")
set(LIBEXEC_PATH "${QT6_INSTALL_PREFIX}/${QT6_INSTALL_LIBEXECS}")
add_custom_target("licensedocrsts"
${CMAKE_CURRENT_LIST_DIR}/qtattributionsscannertorst.py
-l "${LIBEXEC_PATH}"
${CMAKE_CURRENT_LIST_DIR}/../../..
- ${CMAKE_CURRENT_BINARY_DIR}/rst/licenses.rst
+ ${CMAKE_CURRENT_BINARY_DIR}/${DOC_BASE_DIR}/licenses.rst
COMMENT "Creating 3rdparty license documentation..."
)
QCoreApplication should be shut down after asyncio has finished. It is
possible for asyncio to finish while the QCoreApplication is kept alive.
+An argument ``handle_sigint`` determines whether QtAsyncio should handle
+SIGINT (Ctrl+C) and shut down the event loop when it is received. The
+default is ``False``. Set this to ``True`` if you want QtAsyncio to take
+care of handling SIGINT instead of your program.
+
Coroutines explained
^^^^^^^^^^^^^^^^^^^^
text-decoration: none;
padding: .375rem .75rem;
}
+
+dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple):first-child > dt {
+ font-size: +2.25rem;
+ font-weight: 700;
+ color: #ff00dd;
+}
+
+.theme-toggle svg{
+ width: +1.25rem;
+ height: +2.25rem;
+}
+
+.sd-card-title code span {
+ font-size: +1rem;
+ color: var(--color-brand-primary);
+}
--- /dev/null
+My tags: Android
+################
+
+.. toctree::
+ :maxdepth: 1
+ :caption: With this tag
+
+ ../examples/example_bluetooth_heartrate_game.rst
+ ../examples/example_bluetooth_lowenergyscanner.rst
+ ../examples/example_location_mapviewer.rst
+ ../examples/example_multimedia_audiooutput.rst
+ ../examples/example_multimedia_audiosource.rst
+ ../examples/example_multimedia_camera.rst
+ ../examples/example_qml_editingmodel.rst
+ ../examples/example_qml_usingmodel.rst
+ ../examples/example_quick_models_objectlistmodel.rst
+ ../examples/example_quick_models_stringlistmodel.rst
+ ../examples/example_quick_painteditem.rst
+ ../examples/example_quickcontrols_contactslist.rst
+ ../examples/example_quickcontrols_gallery.rst
+ ../examples/example_widgets_widgets_digitalclock.rst
--- /dev/null
+:orphan:
+
+.. _tagoverview:
+
+Tags overview
+#############
+
+.. toctree::
+ :caption: Tags
+ :maxdepth: 1
+
+ Android (14) <android.rst>
Complementary to the wheels, you will be able to download the sources
as well.
-.. note:: Wheels installed this way will be detectable by `Qt Creator`_, which
+.. note:: Wheels installed this way will be detectable by `*Qt Creator*`_, which
will offer you to install them for your current Python interpreter.
Using account.qt.io
Qt Creator Integration
----------------------
-Qt Creator offers the option to create new |project| projects from the main
+*Qt Creator* offers the option to create new |project| projects from the main
wizard.
To execute the projects, make sure that the proper *Python Interpreter* is
-selected, so Qt Creator can use the commercial modules you just installed.
+selected, so *Qt Creator* can use the commercial modules you just installed.
Go to *Edit -> Preferences* where you can find the *Python* option
that will show the following:
``path_to_your_env/bin/python`` (macOS and Linux), or
``path_to_your_env\python.exe`` (Windows).
-As an alternative, you can launch Qt Creator from within the virtual
+As an alternative, you can launch *Qt Creator* from within the virtual
environment, detecting your installation automatically.
Migrating from other versions
'sphinx.ext.coverage', 'sphinx.ext.intersphinx', 'sphinx.ext.todo',
'sphinx.ext.graphviz', 'inheritance_diagram', 'pysideinclude',
'sphinx.ext.viewcode',
- 'sphinx_design', 'sphinx_copybutton', 'myst_parser', 'sphinx_tags',]
+ 'sphinx_design', 'sphinx_copybutton', 'myst_parser', 'sphinx_tags',
+ 'sphinx_toolbox.decorators']
myst_enable_extensions = [
"amsmath",
add_module_names = False
# Skip some warnings when building the documentation with
-# 'build_rst_docs' due to the lack of qdoc generated files, in charge
+# 'build_base_docs' due to the lack of qdoc generated files, in charge
# of sphinx modules (autodoc) and references.
if @SKIP_SPHINX_WARNINGS@:
suppress_warnings = ["autodoc", "autodoc.import_object", "ref.ref"]
because there is no surrounding PySide class that provides the forgiving mode
implementation. Typically, the needed changes are easily found because they often occur
in an import statement.
+
+Permission API
+--------------
+
+The cross-platform permission APIs were introduced to Qt in version 6.5 which are currently relevant
+to platforms macOS, iOS, Android and WebAssembly. With this API, your Qt application can check and
+request permission for certain features like Camera, Microphone, Location, Bluetooth, Contacts,
+Calendar. More about permission API can be read in this `Blog post`_.
+
+When a PySide6 application that uses the permission API is run in interpreted mode, i.e.,
+``python <main_file>.py``, the code implementing the permission API *will not work*. The only way
+to make your PySide6 application using permission API work is to bundle the application. For Android,
+this means using the `pyside6-android-deploy`_ tool and for macOS, this means using the
+`pyside6-deploy`_ tool.
+
+When running in interpreted mode, you can skip over the permission check/request using the following
+*if* condition
+
+::
+
+ is_deployed = "__compiled__" in globals()
+ if not is_deployed and sys.platform == "darwin":
+ # code implementing permission check and request
+
+This can also be seen in the PySide6 `Camera example`_. * __compiled__ * is a Nuitka attribute to
+check if the application is run as a standalone application or run in interpreted mode with Python.
+
+Android
+~~~~~~~~
+
+For Android, `pyside6-android-deploy`_ takes care of identifying the necessary permissions needed by
+the application and adding those permissions to the *AndroidManifest.xml* using the
+*<uses-permission>* element.
+
+macOS
+~~~~~
+
+Since the Android platform does not automatically come bundled with a Python interpreter, it is
+evident that to make a PySide6 application run on Android you have to package the PySide6
+application. This is not the case for desktop platforms like macOS where a Python interpreter and
+its packages can be installed and run quite easily.
+
+The problem for macOS is that for the permission API to work you need a macOS bundle with an
+*Info.plist* file that lists all the permissions required using the *usage description* string for
+each permission used. When Python is run in interpreted mode, i.e., when you run Python, the Qt
+permission API fetches the *Info.plist* from the Python interpreter by default which does not
+contain the *usage description* strings for the permissions required. You can certainly modify the
+*Info.plist* of the Python framework installation to make the Qt permission API work when running
+a PySide6 application from the terminal. However, this is not recommended. Therefore, the only
+viable solution is to bundle the PySide6 application as a macOS application bundle using
+`pyside6-deploy`_. This macOS application bundle will have its own Info.plist file.
+
+.. _`Blog post`: https://www.qt.io/blog/permission-apis-in-qt-6.5
+.. _`Camera Example`: https://doc.qt.io/qtforpython-6/examples/example_multimedia_camera.html#camera-example
+.. _`pyside6-android-deploy`: https://doc.qt.io/qtforpython-6/gettingstarted/package_details.html#deployment
+.. _`pyside6-deploy`: https://doc.qt.io/qtforpython-6/gettingstarted/package_details.html#deployment
commercial/index.rst
gettingstarted/index.rst
api.rst
+ tools/index.rst
tutorials/index.rst
examples/index.rst
videos.rst
--- /dev/null
+.. _pyside6-android-deploy:
+
+pyside6-android-deploy: the Android deployment tool for Qt for Python
+#####################################################################
+
+``pyside6-android-deploy`` is an easy-to-use tool for deploying PySide6 applications to different
+Android architectures, namely *arm64-v8a, x86_64, x86 and armeabi-v7a*. This tool works similarly to
+the ``pyside6-deploy`` tool and uses the same configuration file ``pysidedeploy.spec`` as
+``pyside6-deploy`` to configure the deployment process. Using the deployment configuration
+options either from the command line or from ``pysidedeploy.spec``, ``pyside6-android-deploy``
+configures the deployment to be initiated and invokes `buildozer`_, a tool used for packaging Python
+applications to Android.
+
+The final output is a `.apk` or a `.aab` file created within the project's source directory. The
+`mode` option specified under the :ref:`buildozer <buildozer_key>` key in ``pysidedeploy.spec``
+determines whether a `.apk` or a `.aab` is created.
+
+.. warning:: Currently, users are required to cross-compile Qt for Python to generate the wheels
+ required for a specific Android target architecture. This requirement will disappear when
+ there are official Qt for Python Android wheels (*in progress*). Because of this
+ requirement ``pyside6-android-deploy`` will be considered in **Technical Preview**.
+ Instructions on cross-compiling Qt for Python for Android can be found
+ :ref:`here <cross_compile_android>`.
+
+.. note:: ``pyside6-android-deploy`` only works on a Linux host at the moment. This constraint
+ is also because Qt for Python cross-compilation for Android currently only works on Linux
+ systems.
+
+How to use it?
+==============
+
+Like ``pyside6-deploy``, there are :ref:`two different ways <how_pysidedeploy>` with which
+you can deploy your PySide6 application using ``pyside6-android-deploy``. The only difference is
+that for ``pyside6-android-deploy`` to work, the main Python entry point file should be named
+``main.py``.
+
+.. _pysideandroiddeploy:
+
+pysidedeploy.spec
+=================
+
+Like ``pyside6-deploy``, you can use the ``pysidedeploy.spec`` file to control the various
+parameters of the deployment process. The file has multiple sections, with each section containing
+multiple keys (parameters being controlled) assigned to a value. The advantages of such a file are
+mentioned :ref:`here <pysidedeployspec_advantages>`. The benefit of using the same
+``pysidedeploy.spec`` for both ``pyside6-deploy`` and ``pyside6-android-deploy`` is that you can
+have one single file to control deployment to all platforms.
+
+The relevant parameters for ``pyside6-android-deploy`` are:
+
+**app**
+ * ``title``: The name of the application.
+ * ``project_dir``: Project directory. The general assumption made is that the project directory
+ is the parent directory of the main Python entry point file.
+ * ``input_file``: Path to the main Python entry point file. For ``pyside6-android-deploy`` this
+ file should be named `main.py`.
+ * ``project_file``: If it exists, this points to the path to the `Qt Creator Python Project File
+ .pyproject <https://doc.qt.io/qtforpython-6/faq/typesoffiles.html
+ #qt-creator-python-project-file-pyproject>`_ file. Such a file in the project directory ensures
+ that deployment does not consider unnecessary files when bundling the executable.
+ * ``exec_directory``: The directory where the final executable is generated.
+
+**python**
+ * ``python_path``: Path to the Python executable. It is recommended to run
+ ``pyside6-android-deploy`` from a virtual environment as certain Python packages will be
+ installed onto the Python environment. However, note to keep the created virtual environment
+ outside the project directory so that ``pyside6-android-deploy`` does not try to package it
+ as well.
+ * ``android_packages``: The Python packages installed into the Python environment for deployment
+ to work. By default, the Python packages `buildozer`_ and `cpython`_ are installed.
+
+.. _qt_key:
+
+**qt**
+ * ``modules``: Comma-separated list of all the Qt modules used by the application. Just like the
+ other configuration options in ``pysidedeploy.spec``, this option is also computed automatically
+ by ``pyside6-android-deploy``. However, if you want to explicitly include certain Qt modules,
+ the module names can be appended to this list without the `Qt` prefix.
+ e.g. Network instead of QtNetwork
+ * ``plugins``: This field is *not relevant* for ``pyside6-android-deploy`` and is only specific to
+ ``pyside6-deploy``. The plugins relevant for ``pyside6-android-deploy`` are specified through
+ the ``plugins`` option under the :ref:`android <android_key>` key.
+
+.. _android_key:
+
+**android**
+ * ``wheel_pyside``: Specifies the path to the PySide6 Android wheel for a specific target
+ architecture.
+ * ``wheel_pyside``: Specifies the path to the Shiboken6 Android wheel for a specific target
+ architecture.
+ * ``plugins``: Comma-separated list of all the Qt plugins used by the application. Just like the
+ other configuration options in ``pysidedeploy.spec``, this option is also computed automatically
+ by ``pyside6-android-deploy``. However, if you want to to explicitly include certain Qt plugins,
+ the plugin names can be appended to this list. To see all the plugins bundled with PySide6, see
+ the `plugins` folder in the ``site-packages`` on your Python where PySide6 is installed. The
+ plugin name corresponds to their folder name. This field can be confused with the ``plugins``
+ option under :ref:`qt <qt_key>` key. In the future, they will be merged into one single option.
+
+.. _buildozer_key:
+
+**buildozer**
+ * ``mode``: Specifies one of the two modes - `release` and `debug`, to run `buildozer`_. The
+ `release` mode creates an *aab* while the `debug` mode creates an apk. The default mode is
+ `debug`.
+ * ``recipe_dir``: Specifies the path to the directory containing `python-for-android`_ recipes.
+ This option is automatically computed by ``pyside6-android-deploy`` during deployment. Without
+ the :ref:`--keep-deployment-files <keep_deployment_files>` option of ``pyside6-android-deploy``,
+ the `recipe_dir` will point to a temporary directory that is deleted after the final Android
+ application package is created.
+ * ``jars_dir``: Specifies the path to the Qt Android `.jar` files that are relevant for
+ creating the Android application package. This option is automatically computed by
+ ``pyside6-android-deploy`` during deployment. Just like ``recipe_dir``, this field is also
+ *not relevant* unless used with the :ref:`--keep-deployment-files <keep_deployment_files>`
+ option of ``pyside6-android-deploy``.
+ * ``ndk_path``: Specifies the path to the Android NDK used for packaging the application.
+ * ``sdk_path``: Specifies the path to the Android SDK used for packaging the application.
+ * ``local_libs``: Specifies non-Qt plugins or other libraries compatible with the Android target
+ to be loaded by the Android runtime on startup.
+ * ``sdk_path``: Specifies the path to the Android SDK used for packaging the application.
+ * ``arch``: Specifies the target architecture's instruction set. This option take one of the four
+ values - *aarch64, armv7a, i686, x86_64*.
+
+Command Line Options
+====================
+
+Here are all the command line options of ``pyside6-android-deploy``:
+
+* **-c/--config-file**: This option is used to specify the path to ``pysidedeploy.spec`` explicitly.
+
+* **--init**: Used to only create the ``pysidedeploy.spec`` file.
+ Usage::
+
+ pyside6-android-deploy --init
+
+* **-v/--verbose**: Runs ``pyside6-android-deploy`` in verbose mode.
+
+* **--dry-run**: Displays the commands being run to produce the Android application package.
+
+.. _keep_deployment_files:
+
+* **--keep-deployment-files**: When this option is added, it retains the build folders created by
+ `buildozer`_ during the deployment process. This includes the folder storing the
+ `python-for-android`_ recipes, relevant `.jar` files and even the Android Gradle project for the
+ application.
+
+* **-f/--force**: When this option is used, it assumes ``yes`` to all prompts and runs
+ ``pyside6-android-deploy`` non-interactively. ``pyside6-android-deploy`` prompts the user to
+ create a Python virtual environment, if not already in one. With this option, the current Python
+ environment is used irrespective of whether the current Python environment is a virtual
+ environment or not.
+
+* **--name**: Application name.
+
+* **--wheel-pyside**: Path to the PySide6 Android wheel for a specific target architecture.
+
+* **--wheel-shiboken**: Path to the Shiboken6 Android wheel for a specific target architecture.
+
+* **--ndk-path**: Path to the Android NDK used for packaging the application.
+
+* **--sdk-path**: Path to the Android SDK used for packaging the application.
+
+* **--extra-ignore-dirs**: Comma-separated directory names inside the project directory. These
+ directories will be skipped when searching for Python files relevant to the project.
+
+* **--extra-modules**: Comma-separated list of Qt modules to be added to the application,
+ in case they are not found automatically. The module name can either be specified
+ by omitting the prefix of Qt or including it eg: both Network and QtNetwork works.
+
+.. _cross_compile_android:
+
+Cross-compile Qt for Python wheels for Android
+==============================================
+
+The cross-compilation of Qt for Python wheel for a specific Android target architecture needs to be
+done only once per Qt version, irrespective of the number of applications you are deploying.
+Currently, cross-compiling Qt for Python wheels only works with a Linux host. Follow these steps
+to cross-compile Qt for Python Android wheels.
+
+#. `Download <qt_download>`_ and install Qt version for which you would like to create Qt for Python
+ wheels.
+
+#. Cloning the Qt for Python repository::
+
+ git clone https://code.qt.io/pyside/pyside-setup
+
+#. Check out the version that you want to build, for example 6.7. The version checked out has
+ to correspond to the Qt version downloaded in Step 1::
+
+ cd pyside-setup && git checkout 6.7
+
+#. Installing the dependencies::
+
+ pip install -r requirements.txt
+ pip install -r tools/cross_compile_android/requirements.txt
+
+#. Run the cross-compilation Python script.::
+
+ python tools/cross_compile_android/main.py --plat-name=aarch64 --qt-install-path=/opt/Qt/6.7.0
+ --auto-accept-license --skip-update
+
+ *--qt-install-path* refers to the path where Qt 6.7.0 is installed. *--auto-accept-license* and
+ *--skip-update* are required for downloading and installing Android NDK and SDK if not already
+ specified through command line options or if they don't already exist in the
+ ``pyside6-android-deploy`` cache. Use --help to see all the other available options::
+
+ python tools/cross_compile_android/main.py --help
+
+.. _`buildozer`: https://buildozer.readthedocs.io/en/latest/
+.. _`python-for-android`: https://python-for-android.readthedocs.io/en/latest/
+.. _`qt_download`: https://www.qt.io/download
+.. _`cpython`: https://pypi.org/project/Cython/
platforms. It is a wrapper around `Nuitka <https://nuitka.net/>`_, a Python compiler that
compiles your Python code to C code, and links with libpython to produce the final executable.
-The final executable produced has a ``.exe`` suffix on Windows. For Linux and macOS, they have a
-``.bin`` suffix.
+The final executable produced has a ``.exe`` suffix on Windows, ``.bin`` on Linux and ``.app`` on
+macOS.
+
+.. note:: Although using a virtual environment for Python is recommended for ``pyside6-deploy``, do
+ not add the virtual environment to the application directory you are trying to deploy.
+ ``pyside6-deploy`` will try to package this venv folder and will eventually fail.
+
+.. note:: The default version of Nuitka used with the tool is version ``2.3.2``. This can be
+ updated to a newer version by updating your ``pysidedeploy.spec`` file.
+
+.. _how_pysidedeploy:
How to use it?
==============
multiple keys (parameters being controlled) assigned to a value. The advantages of such a file are
two folds:
+.. _pysidedeployspec_advantages:
+
#. Using the command line, you can control the deployment parameters without specifying them each
- time.. It is saved permanently in a file, and any subsequent runs much later in time
+ time. It is saved permanently in a file, and any subsequent runs much later in time
would enable the user to be aware of their last deployment parameters.
#. Since these parameters are saved into a file, they can be checked into version control. This
#qt-creator-python-project-file-pyproject>`_ file. Such a file makes sure that the deployment
process never considers unnecessary files when bundling the executable.
* ``exec_directory``: The directory where the final executable is generated.
+ * ``icon``: The icon used for the application. For Windows, the icon image should be of ``.ico``
+ format, for macOS it should be of ``.icns`` format, and for linux all standard image formats
+ are accepted.
**python**
* ``python_path``: Path to the Python executable. It is recommended to run the deployment
The reason why only the presence of the above 6 Qt modules is searched for is because they
have the most size heavy binaries among all the Qt modules. With this, you can drastically
reduce the size of your executables.
+ * ``modules``: Comma-separated list of all the Qt modules used by the application. Just like the
+ other configuration options in `pysidedeploy.spec`, this option is also computed automatically
+ by ``pyside6-deploy``. However, if the user wants to explicitly include certain Qt modules, the
+ module names can be appended to this list without the `Qt` prefix.
+ e.g. Network instead of QtNetwork
+ * ``plugins``: Comma-separated list of all the Qt plugins used by the application. Just like the
+ other configuration options in `pysidedeploy.spec`, this option is also computed automatically
+ by ``pyside6-deploy``. However, if the user wants to explicitly include certain Qt plugins,
+ the plugin names can be appended to this list. To see all the plugins bundled with PySide6,
+ see the `plugins` folder in the `site-packages` on your Python where PySide6 is installed. The
+ plugin name correspond to their folder name.
**nuitka**
+ * ``macos.permissions``: Only relevant for macOS. This option lists the permissions used by the
+ macOS application, as found in the ``Info.plist`` file of the macOS application bundle, using
+ the so-called UsageDescription strings. The permissions are normally automatically found by
+ ``pyside6-deploy``. However the user can also explicitly specify them using the format
+ `<UsageDescriptionKey>:<Short Description>`. For example, the Camera permission is specified
+ as::
+
+ NSCameraUsageDescription:CameraAccess
+
* ``extra_args``: Any extra Nuitka arguments specified. It is specified as space-separated
command line arguments i.e. just like how you would specify it when you use Nuitka through
the command line. By default, it contains the following arguments::
pyside6-deploy /path/to/main --init
-* **-v/--verbose**: Runs ``pyside6-deploy`` in verbose mode
+* **-v/--verbose**: Runs ``pyside6-deploy`` in verbose mode.
-* **--dry-run**: Displays the final Nuitka command being run
+* **--dry-run**: Displays the final Nuitka command being run.
* **--keep-deployment-files**: When this option is added, it retains the build folders created by
Nuitka during the deployment process.
``pyside6-deploy`` prompts the user to create a Python virtual environment, if not already in one.
With this option, the current Python environment is used irrespective of whether the current
Python environment is a virtual environment or not.
+
+* **--name**: Application name.
+
+* **--extra-ignore-dirs**: Comma-separated directory names inside the project directory. These
+ directories will be skipped when searching for Python files relevant to the project.
+
+* **--extra-modules**: Comma-separated list of Qt modules to be added to the application,
+ in case they are not found automatically. The module name can either be specified
+ by omitting the prefix of Qt or including it eg: both Network and QtNetwork works.
+
+Considerations
+===============
+
+For deployment to work efficiently by bundling only the necessary plugins, the following utilities
+are required to be installed on the system:
+
+.. list-table::
+ :header-rows: 1
+
+ * - OS
+ - Dependencies
+ - Installation
+ * - Windows
+ - dumpbin
+ - Shipped with MSVC. Run `vcvarsall.bat` to add it to PATH
+ * - Linux
+ - readelf
+ - Available by default
+ * - macOS
+ - dyld_info
+ - Available by default from macOS 12 and upwards
If you are considering Option 3, then starting with 6.4, we ship a new tool called `pyside6-deploy`
that deploys your PySide6 application to all desktop platforms - Windows, Linux, and macOS. To know
-more about how to use the tool see :ref:`pyside6-deploy`. Additionally, you can also use other
-popular deployment tools shown below:
+more about how to use the tool see :ref:`pyside6-deploy`. For Android deployment, see
+:ref:`pyside6-android-deploy`. Additionally, you can also use other popular deployment tools shown
+below:
* `fbs`_
* `PyInstaller`_
:maxdepth: 2
deployment-pyside6-deploy.rst
+ deployment-pyside6-android-deploy.rst
deployment-fbs.rst
deployment-pyinstaller.rst
deployment-cxfreeze.rst
Adapt to new Qt versions
========================
+Adapting to source changes
+--------------------------
+
The dev branch of PySide is switched to a new Qt minor version
after its API review is finished and the API is stable.
The process consists of running a build and evaluating the log file.
The script
`shiboken2tasks.py <https://code.qt.io/cgit/qt-creator/qt-creator.git/tree/scripts/shiboken2tasks.py>`_
-from the Qt Creator repository can be used to convert the shiboken warnings
+from the *Qt Creator* repository can be used to convert the shiboken warnings
into a `task file <https://doc.qt.io/qtcreator/creator-task-lists.html>`_
-for display in the build issues pane of Qt Creator.
+for display in the build issues pane of *Qt Creator*.
Warnings about new enumerations will be shown there; they should be added
to type system file using a ``since`` attribute.
The resolution needs to be decided for each individual case,
mostly by removing old functions and using ``<declare-function>``
to declare new API.
+
+Bumping the version
+-------------------
+
+To instruct ``COIN`` to use the next version of Qt, adapt the files
+``coin/dependencies.yaml`` and/or ``product_dependencies.yaml`` accordingly.
+Next, the wheel names should be changed by adapting
+``sources/shiboken6/.cmake.conf`` and ``sources/pyside6/.cmake.conf``.
- Add it to ``build_scripts/wheel_files.py`` (plugins, translations).
- Copy an existing module to ``sources/pyside6/PySide6/<name>``.
- Adapt the ``typesystem.xml`` and ``CMakeList.txt`` (using for example
- Qt Creator's case-preserving replace function).
+ *Qt Creator*'s case-preserving replace function).
- Make sure the dependencies are correct.
- Find the exported public classes, add them to the ``typesystem.xml`` file,
checking whether they are ``value-type`` or ``object-type``. Add their enums
typically hidden behind a progress message.
- A convenient way of doing this is using
``qt-creator/scripts/shiboken2tasks.py`` from the
- `Qt Creator repository <https://code.qt.io/cgit/qt-creator/qt-creator.git>`_
- converting them to a ``.tasks`` file which can be loaded into Qt Creator's
+ `*Qt Creator* repository <https://code.qt.io/cgit/qt-creator/qt-creator.git>`_
+ converting them to a ``.tasks`` file which can be loaded into *Qt Creator*'s
issue pane.
- Link errors may manifest when ``generate_pyi`` imports the module trying
to create signatures. They indicate a missing source file entry
- Add the tool in ``sources/pyside-tools/pyside_tool.py``.
- Add the tool in ``build_scripts/__init__.py`` to create the setuptools entry points
i.e. this enable using the tool from the console as "pyside6-<tool_name>"
-- Add an entry to ``sources/pyside6/doc/gettingstarted/package_details.rst``.
+- Add an entry to ``sources/pyside6/doc/tools/index.rst`` and the detailed
+ documentation to ``sources/pyside6/doc/tools/<tool_name>.rst``.
- Include the necessary Qt binaries explicitly on ``build_scripts/wheel_files.py``
-- Build with ``--standalone``, verify it is working.
+- Add the necessary files to ``build_scripts/wheel_files.py``.
+- Build with ``--standalone``, verify it is working. Also, check if the wheel bundles the tool.
- Consider using ``build_scripts/qp5_tool.py``.
+Build with address sanitizer (Linux)
+====================================
+
+ASAN needs to be told to not exit on memory leaks and its library
+needs to be pre-loaded. Assuming the library is found
+at ``/usr/lib/gcc/x86_64-linux-gnu/11``:
+
+.. code-block:: bash
+
+ export ASAN_OPTIONS=detect_leaks=0
+ export LD_PRELOAD=/usr/lib/gcc/x86_64-linux-gnu/11/libasan.so
+ python setup.py build [...] --sanitize-address
+
De-Virtualize the Python Files
==============================
.. currentmodule:: PySide6.QtCore
-.. _ClassInfo:
+.. py:decorator:: ClassInfo
-ClassInfo
-*********
-
-This class is used to associate extra information to the class, which is available
-using QObject.metaObject(). Qt and PySide doesn't use this information.
+This decorator is used to associate extra information to the class, which is available
+using ``QObject.metaObject()``. This information is used by the
+*Qt D-Bus* and *Qt Qml* modules.
The extra information takes the form of a dictionary with key and value in a literal string.
it is also possible to pass a python dictionary with arbitrary strings for both the key and
value and enabling special characters in the key.
-.. note:: This Class is a implementation of Q_CLASSINFO macro.
+.. note:: This decorator is a implementation of the Q_CLASSINFO macro.
Example
.. currentmodule:: PySide6.QtCore
-.. _Property:
-
-Property
-********
+.. py:class:: Property
Detailed Description
--------------------
.. currentmodule:: PySide6.QtCore
-.. _QEnum:
-
-QEnum/QFlag
-***********
-
-This class decorator is equivalent to the `Q_ENUM` macro from Qt.
-The decorator is used to register an Enum to the meta-object system,
-which is available via `QObject.staticMetaObject`.
-The enumerator must be in a QObject derived class to be registered.
+.. py:decorator:: QEnum
+This class decorator is equivalent to the `Q_ENUM` macro from Qt. The decorator
+is used to register a Python Enum derived class to the meta-object system,
+which is available via `QObject.staticMetaObject`. The enumerator must be in a
+QObject derived class to be registered.
Example
-------
::
- from enum import Enum, Flag, auto
+ from enum import Enum, auto
- from PySide6.QtCore import QEnum, QFlag, QObject
+ from PySide6.QtCore import QEnum, QObject
class Demo(QObject):
class Orientation(Enum):
North, East, South, West = range(4)
- class Color(Flag):
- RED = auto()
- BLUE = auto()
- GREEN = auto()
- WHITE = RED | BLUE | GREEN
-
- QFlag(Color) # identical to @QFlag usage
-
-
-Caution:
---------
-
-QEnum registers a Python Enum derived class.
-QFlag treats a variation of the Python Enum, the Flag class.
-
-Please do not confuse that with the Qt QFlags concept. Python does
-not use that concept, it has its own class hierarchy, instead.
-For more details, see the `Python enum documentation <https://docs.python.org/3/library/enum.html>`_.
-
-
-Details about Qt Flags:
------------------------
-
-There are some small differences between Qt flags and Python flags.
-In Qt, we have for instance these declarations:
-
-::
-
- enum QtGui::RenderHint { Antialiasing, TextAntialiasing, SmoothPixmapTransform,
- HighQualityAntialiasing, NonCosmeticDefaultPen }
- flags QtGui::RenderHints
-
-The equivalent Python notation would look like this:
-
-::
-
- @QFlag
- class RenderHints(enum.Flag)
- Antialiasing = auto()
- TextAntialiasing = auto()
- SmoothPixmapTransform = auto()
- HighQualityAntialiasing = auto()
- NonCosmeticDefaultPen = auto()
-
-
-As another example, the Qt::AlignmentFlag flag has 'AlignmentFlag' as the enum
-name, but 'Alignment' as the type name. Non flag enums have the same type and
-enum names.
-
-::
-
- enum Qt::AlignmentFlag
- flags Qt::Alignment
-
-The Python way to specify this would be
-
-::
-
- @QFlag
- class Alignment(enum.Flag):
- ...
+See :deco:`QFlag` for registering Python Flag derived classes.
-Meanwhile we have converted all enums and flags to Python Enums (optional in ``PySide 6.3``,
-default in ``PySide 6.4``), see the :ref:`NewEnumSystem` section.
+Meanwhile all enums and flags have been converted to Python Enums
+(default since ``PySide 6.4``), see the :ref:`NewEnumSystem` section.
--- /dev/null
+.. currentmodule:: PySide6.QtCore
+.. py:decorator:: QFlag
+
+QFlag handles a variation of the Python Enum, the Flag class.
+
+Please do not confuse that with the Qt QFlags concept. Python does
+not use that concept, it has its own class hierarchy, instead.
+For more details, see the `Python enum documentation <https://docs.python.org/3/library/enum.html>`_.
+
+Example
+-------
+
+::
+
+ from enum import Flag, auto
+
+ from PySide6.QtCore import QFlag, QObject
+
+ class Demo(QObject):
+
+ @QFlag
+ class Color(Flag):
+ RED = auto()
+ BLUE = auto()
+ GREEN = auto()
+ WHITE = RED | BLUE | GREEN
+
+
+Details about Qt Flags:
+-----------------------
+
+There are some small differences between Qt flags and Python flags.
+In Qt, we have for instance these declarations:
+
+::
+
+ enum QtGui::RenderHint { Antialiasing, TextAntialiasing, SmoothPixmapTransform,
+ HighQualityAntialiasing, NonCosmeticDefaultPen }
+ flags QtGui::RenderHints
+
+The equivalent Python notation would look like this:
+
+::
+
+ @QFlag
+ class RenderHints(enum.Flag)
+ Antialiasing = auto()
+ TextAntialiasing = auto()
+ SmoothPixmapTransform = auto()
+ HighQualityAntialiasing = auto()
+ NonCosmeticDefaultPen = auto()
+
+
+As another example, the Qt::AlignmentFlag flag has 'AlignmentFlag' as the enum
+name, but 'Alignment' as the type name. Non flag enums have the same type and
+enum names.
+
+::
+
+ enum Qt::AlignmentFlag
+ flags Qt::Alignment
+
+The Python way to specify this would be
+
+::
+
+ @QFlag
+ class Alignment(enum.Flag):
+ ...
+
+See :deco:`QEnum` for registering Python Enum derived classes.
+
+Meanwhile all enums and flags have been converted to Python Enums
+(default since ``PySide 6.4``), see the :ref:`NewEnumSystem` section.
.. currentmodule:: PySide6.QtCore
-.. _Signal:
-
-Qt Signal
-*********
+.. py:class:: Signal
Synopsis
--------
.. currentmodule:: PySide6.QtCore
-.. _Slot:
+.. py:decorator:: Slot([type1 [, type2...]] [, name="" [, result=None, [tag=""]]])
-Qt Slots
-********
+ :param name: str
+ :param result: type
+ :param tag: str
-Detailed Description
---------------------
+``Slot`` takes a list of Python types of the arguments.
- PySide6 adopt PyQt's new signal and slot syntax as-is. The PySide6
- implementation is functionally compatible with the PyQt one, with the
- exceptions listed below.
+The optional named argument ``name`` defines the slot name. If nothing is
+passed, the slot name will be the decorated function name.
- PyQt's new signal and slot style utilizes method and decorator names
- specific to their implementation. These will be generalized according to
- the table below:
+The optional named argument ``result`` specifies the return type.
- ======= ======================= =============
- Module PyQt factory function PySide class
- ======= ======================= =============
- QtCore pyqtSignal Signal
- QtCore pyqtSlot Slot
- ======= ======================= =============
+The optional named argument ``tag`` specifies a value to be returned
+by ``QMetaMethod.tag()``.
- .. class:: PySide6.QtCore.Slot([type1 [, type2...]] [, name="" [, result=None, [tag=""]]])
+This implementation is functionally compatible with the PyQt one.
- :param name: str
- :param result: type
- :param tag: str
+======= =========== ======
+Module PyQt PySide
+======= =========== ======
+QtCore pyqtSignal Signal
+QtCore pyqtSlot Slot
+======= =========== ======
- ``Slot`` takes a list of Python types of the arguments.
-
- The optional named argument ``name`` defines the slot name. If nothing is
- passed, the slot name will be the decorated function name.
-
- The optional named argument ``result`` specifies the return type.
-
- The optional named argument ``tag`` specifies a value to be returned
- by ``QMetaMethod.tag()``.
-
- .. seealso:: :ref:`signals-and-slots`
+.. seealso:: :ref:`signals-and-slots`
Q_INVOKABLE
-----------
- There is no equivalent of the Q_INVOKABLE macro of Qt
- since PySide6 slots can actually have return values.
- If you need to create a invokable method that returns some value,
- declare it as a slot, e.g.:
-
- ::
+There is no equivalent of the Q_INVOKABLE macro of Qt
+since PySide6 slots can actually have return values.
+If you need to create a invokable method that returns some value,
+declare it as a slot, e.g.:
- class Foo(QObject):
+::
- @Slot(float, result=int)
- def getFloatReturnInt(self, f):
- return int(f)
+ class Foo(QObject):
+ @Slot(float, result=int)
+ def getFloatReturnInt(self, f):
+ return int(f)
.. currentmodule:: PySide6.QtDesigner
-.. _QPyDesignerContainerExtension:
-
-QPyDesignerContainerExtension
-*****************************
+.. py:class:: QPyDesignerContainerExtension
QPyDesignerContainerExtension is the base class for implementing
`QDesignerContainerExtension class`_
-for a Qt Designer custom widget plugin in Python.
+for a *Qt Widgets Designer* custom widget plugin in Python.
It provides the required inheritance from **QObject**.
.. _QDesignerContainerExtension class: https://doc.qt.io/qt-6/qdesignercontainerextension.html
.. currentmodule:: PySide6.QtDesigner
-.. _QPyDesignerCustomWidgetCollection:
-
-QPyDesignerCustomWidgetCollection
-*********************************
+.. py:class:: QPyDesignerCustomWidgetCollection
Synopsis
--------
.. py:staticmethod:: QPyDesignerCustomWidgetCollection.registerCustomWidget(type[, xml=""[, tool_tip=""[, icon=""[, group=""[container=False]]]]])
- Registers an instance of a Python-implemented QWidget by type with Qt Designer.
+ Registers an instance of a Python-implemented QWidget by type with
+ *Qt Widgets Designer*.
The optional keyword arguments correspond to the getters of
`QDesignerCustomWidgetInterface`_ :
Adds a custom widget (implementation of
`QDesignerCustomWidgetInterface`_)
- with Qt Designer.
+ with *Qt Widgets Designer*.
:param QDesignerCustomWidgetInterface custom_widget: Custom widget instance
.. currentmodule:: PySide6.QtDesigner
-.. _QPyDesignerMemberSheetExtension:
-
-QPyDesignerMemberSheetExtension
-*******************************
+.. py:class:: QPyDesignerMemberSheetExtension
QPyDesignerMemberSheetExtension is the base class for implementing
`QDesignerMemberSheetExtension class`_
-for a Qt Designer custom widget plugin in Python.
+for a *Qt Widgets Designer* custom widget plugin in Python.
It provides the required inheritance from **QObject**.
.. _QDesignerMemberSheetExtension class: https://doc.qt.io/qt-6/qdesignermembersheetextension.html
.. currentmodule:: PySide6.QtDesigner
-.. _QPyDesignerTaskMenuExtension:
-
-QPyDesignerTaskMenuExtension
-****************************
+.. py:class:: QPyDesignerTaskMenuExtension
QPyDesignerTaskMenuExtension is the base class for implementing
`QDesignerTaskMenuExtension class`_
-for a Qt Designer custom widget plugin in Python.
+for a *Qt Widgets Designer* custom widget plugin in Python.
It provides the required inheritance from **QObject**.
.. _QDesignerTaskMenuExtension class: https://doc.qt.io/qt-6/qdesignertaskmenuextension.html
-Provides classes to create your own custom widget plugins for Qt Designer and
-classes to access Qt Designer components.
+Provides classes to create your own custom widget plugins for
+*Qt Widgets Designer* and classes to access *Qt Widgets Designer*
+components.
In addition, the :class:`QFormBuilder<PySide6.QtDesigner.QFormBuilder>` class
provides the possibility of constructing user interfaces from UI files at
--- /dev/null
+.. currentmodule:: PySide6.QtQml
+.. py:class:: ListProperty
+
+ The ``ListProperty`` class allows applications to expose list-like properties of
+ :class:`~PySide6.QtCore.QObject`-derived classes to QML.
+ The usage is shown in the :ref:`qml-object-and-list-property-types-example`
+ and the :ref:`qml-chapter5-listproperties` example.
+
+ .. py:method:: __init__(type, append, count=None, at=None, clear=None, removeLast=None, doc="", notify=None, designable=True, scriptable=True, stored=True, user=False, constant=False, final=False)
+
+ :param type type: Element type
+ :param callable append: A function to append an item
+ :param callable count: A function returning the list count
+ :param callable at: A function returning the item at an index
+ :param callable clear: A function to clear the list
+ :param removeLast: A function to remove the last item
+ :param str doc: Doc string
+ :param Signal notify: A signal emitted when a change occurs
+ :param bool designable: Not used in QML
+ :param bool scriptable: Not used in QML
+ :param bool stored: Whether the property is stored
+ :param bool user: Not used in QML
+ :param bool constant: Whether the property is constant
+ :param bool final: Whether the property is final
.. currentmodule:: PySide6.QtQml
-.. _QPyQmlParserStatus:
-
-QPyQmlParserStatus
-******************
+.. py:class:: QPyQmlParserStatus
QPyQmlParserStatus is the base class for implementing
`QQmlParserStatus class`_ .
.. currentmodule:: PySide6.QtQml
-.. _QPyQmlPropertyValueSource:
-
-QPyQmlPropertyValueSource
-*************************
+.. py:class:: QPyQmlPropertyValueSource
QPyQmlPropertyValueSource is the base class for implementing
`QQmlPropertyValueSource class`_ .
.. currentmodule:: PySide6.QtQml
-.. _QmlAnonymous:
-
-QmlAnonymous
-************
-
.. py:decorator:: QmlAnonymous
- Declares the enclosing type to be available, but anonymous in QML. The type
- cannot be created or used to declare properties in QML, but when passed from
- C++, it is recognized. In QML, you can use properties of this type if they
- are declared in C++.
+Declares the enclosing type to be available, but anonymous in QML. The type
+cannot be created or used to declare properties in QML, but when passed from
+C++, it is recognized. In QML, you can use properties of this type if they
+are declared in C++.
- .. code-block:: python
+.. code-block:: python
- QML_IMPORT_NAME = "com.library.name"
- QML_IMPORT_MAJOR_VERSION = 1
- QML_IMPORT_MINOR_VERSION = 0 # Optional
+ QML_IMPORT_NAME = "com.library.name"
+ QML_IMPORT_MAJOR_VERSION = 1
+ QML_IMPORT_MINOR_VERSION = 0 # Optional
- @QmlAnonymous
- class ClassForQml(QObject):
- # ...
+ @QmlAnonymous
+ class ClassForQml(QObject):
+ # ...
.. currentmodule:: PySide6.QtQml
-.. _QmlAttached:
-
-QmlAttached
-***********
-
.. py:decorator:: QmlAttached
This decorator declares that the enclosing type attaches the type passed as
.. currentmodule:: PySide6.QtQml
-.. _QmlElement:
-
-QmlElement
-**********
-
.. py:decorator:: QmlElement
- This decorator registers a class it is attached to for use in QML, using
- global variables to specify the import name and version.
+This decorator registers a class it is attached to for use in QML, using
+global variables to specify the import name and version.
- .. code-block:: python
+.. code-block:: python
- QML_IMPORT_NAME = "com.library.name"
- QML_IMPORT_MAJOR_VERSION = 1
- QML_IMPORT_MINOR_VERSION = 0 # Optional
+ QML_IMPORT_NAME = "com.library.name"
+ QML_IMPORT_MAJOR_VERSION = 1
+ QML_IMPORT_MINOR_VERSION = 0 # Optional
- @QmlElement
- class ClassForQml(QObject):
- # ...
+ @QmlElement
+ class ClassForQml(QObject):
+ # ...
- Afterwards the class may be used in QML:
+Afterwards the class may be used in QML:
- .. code-block:: python
+.. code-block:: python
- import com.library.name 1.0
+ import com.library.name 1.0
- ClassForQml {
- // ...
- }
+ ClassForQml {
+ // ...
+ }
.. currentmodule:: PySide6.QtQml
-.. _QmlExtended:
-
-QmlExtended
-***********
-
.. py:decorator:: QmlExtended
Declares that the enclosing type uses the type passed as an extension to
.. currentmodule:: PySide6.QtQml
-.. _QmlForeign:
-
-QmlForeign
-**********
-
.. py:decorator:: QmlForeign
- This decorator can be used to change the type that is created by QML.
+This decorator can be used to change the type that is created by QML.
- This is useful for registering types that cannot be amended by adding the
- QmlElement decorator, for example because they belong to 3rdparty libraries.
+This is useful for registering types that cannot be amended by adding the
+QmlElement decorator, for example because they belong to 3rdparty libraries.
- .. code-block:: python
+.. code-block:: python
- QML_IMPORT_NAME = "com.library.name"
- QML_IMPORT_MAJOR_VERSION = 1
- QML_IMPORT_MINOR_VERSION = 0 # Optional
+ QML_IMPORT_NAME = "com.library.name"
+ QML_IMPORT_MAJOR_VERSION = 1
+ QML_IMPORT_MINOR_VERSION = 0 # Optional
- @QmlNamedElement("QWidget")
- @QmlForeign(QWidget)
- class ForeignWidgetHelperClass(QObject):
+ @QmlNamedElement("QWidget")
+ @QmlForeign(QWidget)
+ class ForeignWidgetHelperClass(QObject):
...
- Afterwards the class may be used in QML:
+Afterwards the class may be used in QML:
- .. code-block:: javascript
+.. code-block:: javascript
- import com.library.name 1.0
+ import com.library.name 1.0
- QWidget {
- // ...
- }
+ QWidget {
+ // ...
+ }
.. currentmodule:: PySide6.QtQml
-.. _QmlNamedElement:
-
-QmlNamedElement
-***************
-
.. py:decorator:: QmlNamedElement
- This decorator registers a class it is attached to for use in QML under
- a name different from the class name, using global variables to specify
- the import name and version.
+This decorator registers a class it is attached to for use in QML under
+a name different from the class name, using global variables to specify
+the import name and version.
- .. code-block:: python
+.. code-block:: python
- QML_IMPORT_NAME = "com.library.name"
- QML_IMPORT_MAJOR_VERSION = 1
- QML_IMPORT_MINOR_VERSION = 0 # Optional
+ QML_IMPORT_NAME = "com.library.name"
+ QML_IMPORT_MAJOR_VERSION = 1
+ QML_IMPORT_MINOR_VERSION = 0 # Optional
- @QmlNamedElement("ClassForQml")
- class ClassWithSomeName(QObject):
+ @QmlNamedElement("ClassForQml")
+ class ClassWithSomeName(QObject):
...
- Afterwards the class may be used in QML:
+Afterwards the class may be used in QML:
- .. code-block:: javascript
+.. code-block:: javascript
- import com.library.name 1.0
+ import com.library.name 1.0
- ClassForQml {
- // ...
- }
+ ClassForQml {
+ // ...
+ }
.. currentmodule:: PySide6.QtQml
-.. _QmlSingleton:
-
-QmlSingleton decorator
-**********************
-
.. py:decorator:: QmlSingleton
Declares the decorated type to be a singleton in QML. This only takes effect if
-the type is a Q_OBJECT and is available in QML (by having a QmlElement decorator).
+the type is a QObject and is available in QML (by having a QmlElement decorator).
The QQmlEngine will try to create a singleton instance using the type's default
constructor.
class ClassForQml(QObject):
...
+It is also possible to use a static ``create()`` method which receives
+the engine as a parameter:
+
+.. code-block:: python
+
+ @QmlElement
+ @QmlSingleton
+ class ClassForQml(QObject):
+
+ @staticmethod
+ def create(engine):
+ ...
+
.. note:: The order of the decorators matters; ``QmlSingleton`` needs to be preceded by ``QmlElement``.
.. currentmodule:: PySide6.QtQml
-.. _QmlUncreatable:
-
-QmlUncreatable
-**************
-
.. py:decorator:: QmlUncreatable
Declares that the decorated type shall not be creatable from QML. This takes
+++ /dev/null
-.. currentmodule:: PySide6.QtQml
-.. _qmlRegisterSingletonInstance:
-
-qmlRegisterSingletonInstance
-****************************
-
-.. py:function:: qmlRegisterSingletonInstance(pytype: type,\
- uri: str,\
- versionMajor: int,\
- versionMinor: int,\
- typeName: str,\
- instanceObject: object) -> int
-
- :param type pytype: Python class
- :param str uri: uri to use while importing the component in QML
- :param int versionMajor: major version
- :param int versionMinor: minor version
- :param str typeName: name exposed to QML
- :param object instanceObject: singleton object to be registered
- :return: int (the QML type id)
-
- This function registers a singleton Python object *instanceObject*, with a particular *uri* and
- *typeName*. Its version is a combination of *versionMajor* and *versionMinor*.
-
- Use this function to register an object of the given type *pytype* as a singleton type.
+++ /dev/null
-.. currentmodule:: PySide6.QtQml
-.. _qmlRegisterSingletonType:
-
-qmlRegisterSingletonType
-************************
-
-.. py:function:: qmlRegisterSingletonType(pytype: type, uri: str, versionMajor: int, versionMinor: int, typeName: str) -> int
-
- :param type pytype: Python class
- :param str uri: uri to use while importing the component in QML
- :param int versionMajor: major version
- :param int versionMinor: minor version
- :param str typeName: name exposed to QML
- :return: int (the QML type id)
-
- This function registers a Python type as a singleton in the QML system.
-
- Alternatively, the :ref:`QmlSingleton` decorator can be used.
-
-.. py:function:: qmlRegisterSingletonType(pytype: type, uri: str, versionMajor: int, versionMinor: int, typeName: str, callback: object) -> int
-
- :param type pytype: Python class
- :param str uri: uri to use while importing the component in QML
- :param int versionMajor: major version
- :param int versionMinor: minor version
- :param str typeName: name exposed to QML
- :param object callback: Python callable (to handle Python type)
- :return: int (the QML type id)
-
- This function registers a Python type as a singleton in the QML system using
- the provided callback (which gets a QQmlEngine as a parameter) to generate
- the singleton.
-
-
-.. py:function:: qmlRegisterSingletonType(uri: str, versionMajor: int, versionMinor: int, typeName: str, callback: object) -> int
-
- :param str uri: uri to use while importing the component in QML
- :param int versionMajor: major version
- :param int versionMinor: minor version
- :param str typeName: name exposed to QML
- :param object callback: Python callable (to handle QJSValue)
- :return: int (the QML type id)
-
- This function registers a QJSValue as a singleton in the QML system using
- the provided callback (which gets a QQmlEngine as a parameter) to
- generate the singleton.
+++ /dev/null
-.. currentmodule:: PySide6.QtQml
-.. _qmlRegisterType:
-
-qmlRegisterType
-***************
-
-.. py:function:: qmlRegisterType(pytype: type, uri: str, versionMajor: int, versionMinor: int, qmlName: str) -> int
-
- :param type pytype: Python class
- :param str uri: uri to use while importing the component in QML
- :param int versionMajor: major version
- :param int versionMinor: minor version
- :param str qmlName: name exposed to QML
- :return: int (the QML type id)
-
- This function registers the Python *type* in the QML system with the
- name *qmlName*, in the library imported from *uri* having the
- version number composed from *versionMajor* and *versionMinor*.
-
- For example, this registers a Python class 'MySliderItem' as a QML
- type named 'Slider' for version '1.0' of a module called
- 'com.mycompany.qmlcomponents':
-
- ::
-
- qmlRegisterType(MySliderItem, "com.mycompany.qmlcomponents", 1, 0, "Slider")
-
- Once this is registered, the type can be used in QML by importing
- the specified module name and version number:
-
- ::
-
- import com.mycompany.qmlcomponents 1.0
-
- Slider { ... }
-
- Note that it's perfectly reasonable for a library to register types
- to older versions than the actual version of the library.
- Indeed, it is normal for the new library to allow QML written to
- previous versions to continue to work, even if more advanced
- versions of some of its types are available.
+++ /dev/null
-.. currentmodule:: PySide6.QtQml
-.. _qmlRegisterUncreatableType:
-
-
-qmlRegisterUncreatableType
-**************************
-
-
-.. py:function:: qmlRegisterUncreatableType(pytype: type, uri: str, versionMajor: int, versionMinor: int, qmlName: str, noCreationReason: str) -> int
-
-
- :param type pytype: Python class
- :param str uri: uri to use while importing the component in QML
- :param int versionMajor: major version
- :param int versionMinor: minor version
- :param str qmlName: name exposed to QML
- :param str noCreationReason: Error message shown when trying to create the QML type
- :return: int (the QML type id)
-
- This function registers the Python *type* in the QML system as an uncreatable type with the
- name *qmlName*, in the library imported from *uri* having the
- version number composed from *versionMajor* and *versionMinor*,
- showing *noCreationReason* as an error message when creating the type is attempted.
-
- For example, this registers a Python class 'MySliderItem' as a QML
- type named 'Slider' for version '1.0' of a module called
- 'com.mycompany.qmlcomponents':
-
- ::
- qmlRegisterUncreatableType(MySliderItem, "com.mycompany.qmlcomponents", 1, 0, "Slider", "Slider cannot be created.")
-
- Note that it's perfectly reasonable for a library to register types
- to older versions than the actual version of the library.
- Indeed, it is normal for the new library to allow QML written to
- previous versions to continue to work, even if more advanced
- versions of some of its types are available.
-
- Alternatively, the :ref:`QmlUncreatable` decorator can be used.
--- /dev/null
+ Qt Quick Test is a unit test framework for QML applications. Test cases are
+ written as JavaScript functions within a QML TestCase type:
+
+.. code-block:: JavaScript
+
+ import QtQuick
+ import QtTest
+
+ TestCase {
+ name: "MathTests"
+
+ function test_math() {
+ compare(2 + 2, 4, "2 + 2 = 4")
+ }
+
+ function test_fail() {
+ compare(2 + 2, 5, "2 + 2 = 5")
+ }
+ }
+
+Functions whose names start with ``test_`` are treated as test cases to be
+executed.
+
+QML API
+^^^^^^^
+
+The `QML types <https://doc.qt.io/qt-6/qttest-qmlmodule.html>`_
+in Qt Quick Test are available through the ``QtTest`` import.
+To use the types, add the following import statement to your ``.qml`` file:
+
+.. code-block:: JavaScript
+
+ import QtTest
+
+Running Tests
+^^^^^^^^^^^^^
+
+Test cases are launched by a harness that consists of the following code:
+
+.. code-block:: Python
+
+ import sys
+ from PySide6.QtQuickTest import QUICK_TEST_MAIN
+
+ QUICK_TEST_MAIN("example", sys.argv)
+
+Where "example" is the identifier to use to uniquely identify this set of
+tests.
+
+Test execution can be controlled by a number of command line options (pass
+``-h`` for help).
+
+Executing Code Before QML Tests
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+To execute code before any of the QML tests are run, the
+:py:func:`QUICK_TEST_MAIN_WITH_SETUP` function can be used. This can be useful
+for setting context properties on the QML engine, amongst other things.
+++ /dev/null
-.. currentmodule:: PySide6.QtUiTools
-
-loadUiType
-***********
-
-.. py:function:: loadUiType(uifile: str) -> tuple(object, object)
-
- :param str uifile: The name of the `.ui` file
- :return: tuple(object, object)
-
- This function generates and loads a `.ui` file at runtime, and it returns
- a `tuple` containing the reference to the Python class, and the base class.
-
- We recommend not to use this approach as the workflow should be to generate a Python file
- from the `.ui` file, and then import and load it to use it, but we do understand that
- there are some corner cases when such functionality is required.
-
- The internal process relies on `uic` being in the PATH.
- The `pyside6-uic` wrapper uses a shipped `uic` that is located in the
- `site-packages/PySide6/uic`, so PATH needs to be updated to use that if there
- is no `uic` in the system.
-
- A simple use case is::
-
- from PySide6.QtUiTools import loadUiType
-
- generated_class, base_class = loadUiType("themewidget.ui")
- # the values will be:
- # (<class '__main__.Ui_ThemeWidgetForm'>, <class 'PySide6.QtWidgets.QWidget'>)
-
- widget = base_class()
- form = generated_class()
- form.setupUi(widget)
- # form.a_widget_member.a_method_of_member()
- widget.show()
-Qt Designer forms are processed at run-time to produce
+*Qt Widgets Designer* forms are processed at run-time to produce
dynamically-generated user interfaces. In order to generate a form at
run-time, a resource file containing a UI file is needed.
:meth:`sizePolicy<PySide6.QtWidgets.QWidget.sizePolicy>` properties,
and the layout distributes the available space accordingly.
-:ref:`Qt Designer<using_ui_files>` is a powerful tool for interactively
+:ref:`Qt Widgets Designer<using_ui_files>` is a powerful tool for interactively
creating and arranging widgets in layouts.
Model/View Classes
User Interface Definition File ``.ui``
--------------------------------------
-When using Qt Designer, you can create user interfaces using Qt Widgets with
-the WYSIWYG form editor, this interface is represented as a widget tree using
-XML. Here is an extract of the beginning of a ``.ui`` file:
+When using *Qt Widgets Designer*, you can create user interfaces using
+Qt Widgets with the WYSIWYG form editor, this interface is represented
+as a widget tree using XML. Here is an extract of the beginning of a
+``.ui`` file:
.. code-block:: xml
to QML to be used.
You can write ``.qml`` files by hand, but also you can use tools like the
-QML Designer that is embedded in Qt Creator. Additionally, there are commercial
-tools like Qt Design Studio that allow you to load designs from other design
+*QML Designer* that is embedded in *Qt Creator*. Additionally, there are commercial
+tools like *Qt Design Studio* that allow you to load designs from other design
applications.
Here you can find an example of how a ``.qml`` file looks like.
Qt Creator Python Project File ``.pyproject``
---------------------------------------------
-For Qt Creator to load and handle Python based projects, a special file is
+For *Qt Creator* to load and handle Python based projects, a special file is
needed, because C++ based projects could be handle from ``.qmake`` or
``CMakeLists.txt`` file, which are not used with Python-based projects.
-Old versions of Qt Creator, provided a simple format with the ``.pyqtc``
+Old versions of *Qt Creator*, provided a simple format with the ``.pyqtc``
extension, which were plain-text files with one-file-per-line::
library/server.py
.. note:: You can adapt these applications to use your self-made style, but
you need to be aware that the goal of Widgets is to respect the system
- style, be careful when changing colors. Check this `simple tutorial
- <widgetstyling>`_ on how to do so.
+ style, be careful when changing colors. Check this
+ :ref:`simple tutorial <widgetstyling>` on how to do so.
QML
---
==========================
|project|, as any other Python module, can be used in any Python-compatible
-IDE, but not all of them will provide extra functionality like Qt Creator does.
+IDE, but not all of them will provide extra functionality like *Qt Creator* does.
Besides writing files, there are some external steps you might want to perform
in order to help the development of your applications:
:command:`pyside6-uic -i form.ui -o ui_form.py`
* Generating a Python file from a ``.qrc`` file:
:command:`pyside6-rcc -i resources.qrc -o rc_resources.py`
-* Opening Qt Designer with the command :command:`pyside6-designer` to
- edit/create ``.ui`` files.
+* Opening *Qt Widgets Designer* with the command :command:`pyside6-designer`
+ to edit/create ``.ui`` files.
External add-ons/plugins from your favorite IDE might include configuration
steps to run these commands, or open external tools like Designer and
PyCharm
-------
-You can configure PyCharm to enable external tools, in |project| terms, Qt Designer, and
-Qt Creator. Go to ``File > Settings > tools > PyCharm External Tools``, and include the following
+You can configure PyCharm to enable external tools, in |project| terms,
+*Qt Widgets Designer*, and *Qt Creator*. Go to
+``File > Settings > tools > PyCharm External Tools``, and include the following
information to add them to your project.
-Later, you will be able to right click a ``.ui`` file, and select ``Qt Designer``,
-``pyside6-uic``, or any tool that you configured this way.
+Later, you will be able to right click a ``.ui`` file, and select
+``Qt Widgets Designer``, ``pyside6-uic``, or any tool that you configured this
+way.
you might get them with ``brew``, and on **Windows** you can download the installer from each
website.
-* **Python**: 3.7+ `[official Python website] <https://www.python.org/downloads/>`_
+* **Python**: 3.9+ `[official Python website] <https://www.python.org/downloads/>`_
* **Qt:** 6.4+ `[online installer] <https://download.qt.io/official_releases/online_installers/>`_
* **CMake:** 3.18+ `[official CMake website] <https://cmake.org/download/>`_
* **Git:** 2.0+. `[official Git website] <https://git-scm.com/downloads>`_
:mod:`QtCore <PySide6.QtCore>`, :mod:`QtGui <PySide6.QtGui>`, and
:mod:`QtWidgets <PySide6.QtWidgets>`.
+`CMake Unity Build Mode`_ is used by default for speed-up.
+
Other important options to consider are:
- * ``--unity``, Activates `CMake Unity Build Mode`_, which speeds up the
- build by concatenating source files,
* ``--cmake``, to specify the path to the cmake binary,
* ``--reuse-build``, to rebuild only the modified files,
* ``--openssl=/path/to/openssl/bin``, to use a different path for OpenSSL,
Starting from 5.15, there are two options to build the documentation:
-1. Building rst-only documentation (no API)
+1. Building the base documentation (no API)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The process of parsing Qt headers to generate the PySide API documentation can take several
-minutes, this means that modifying a specific section of the rst files we currently have, might
-become a hard task. You may only care about the ReStructuredText only documentation.
+minutes, this means that modifying a specific section of the documentation we currently have, might
+become a hard task. You may only care about the base documentation, which comprises all the
+documentation except for the API documentation.
To generate this, execute the following command::
- python setup.py build_rst_docs
+ python setup.py build_base_docs
This will generate an ``html/`` directory with the following structure::
This is useful when updating the general sections of the documentation, adding tutorials,
modifying the build instructions, and more.
-.. note:: In case you are interested in generating the Example Gallery, you
- would need to first run ``python tools/example_gallery/main.py`` to
- generate the examples ``rst`` for the gallery.
+.. note:: In case you are interested in only generating the Example Gallery,
+ you would need to run ``python tools/example_gallery/main.py`` to
+ generate the examples ``documentation`` for the gallery. This will
+ also be used internally by the ``build_base_docs`` target
-2. Building the documentation (rst + API)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+2. Building the documentation (Base + API)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The documentation is being generated using **qdoc** to get the API information, and also **sphinx**
for the local Python related notes.
You can add ``-j X``, to perform the build process in parallel with
X processes.
-.. note:: The :command:`apidoc` make target builds offline documentation in ``QCH`` (Qt Creator Help)
- format by default. You can switch to building for the online use with the ``--doc-build-online``
- configure option.
+.. note:: The :command:`apidoc` make target builds offline documentation in ``QCH``
+ (Qt Compressed Help) format by default. You can switch to building for the
+ online use with the ``--doc-build-online`` configure option.
The target executes several steps:
Re-running the command will not execute step 1 unless the file
``qdoc-output/webxml/qtcore-index.webxml`` is removed from the build tree.
-Similarly, step 2 will not be executed unless the file ``rst/PySide6/QtCore/index.rst``
+Similarly, step 2 will not be executed unless the file ``base/PySide6/QtCore/index.rst``
is removed.
Finally, you will get a ``html`` directory containing all the generated documentation. The offline
If you want to temporarily change a ``.rst`` file to examine the impact on
formatting, you can re-run ``sphinx`` in the ``doc`` directory::
- sphinx-build rst html
+ sphinx-build base html
Viewing offline documentation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The offline documentation (QCH) can be viewed using the Qt Creator IDE or Qt Assistant, which is
-a standalone application for viewing QCH files.
+The offline documentation (QCH) can be viewed using the *Qt Creator* IDE or
+*Qt Assistant*, which is a standalone application for viewing QCH files.
-To view the QCH using Qt Creator, following the instructions outlined in
-`Using Qt Creator Help Mode <https://doc.qt.io/qtcreator/creator-help.html>`_. If you chose to
-use Qt Assistant instead, use the following command to register the QCH file before launching
-Qt Assistant::
+To view the QCH using *Qt Creator*, following the instructions outlined in
+`Using Qt Creator Help Mode <https://doc.qt.io/qtcreator/creator-help.html>`_.
+If you chose to use *Qt Assistant* instead, use the following command to register
+the QCH file before launching *Qt Assistant*::
assistant -register PySide.qch
* the default ``QCH`` format is used; in which case the required ``objects.inv``
files are not generated. Use ``--doc-build-online``.
-* rst-only and full doc builds are mixed, resulting in wrong values for the
+* base and full doc builds are mixed, resulting in wrong values for the
intersphinx location in the CMake files. Re-run ``cmake`` to fix this.
Using the internal tools
Adding the full URL because it's a different sphinx project.
.. _`Shiboken Documentation`: https://doc.qt.io/qtforpython/shiboken6/gettingstarted.html
-.. _package_tools:
-
Tools Included
--------------
-Following the same idea from the modules, we also include in the packages
-(wheels) Qt tools that are important for any Qt application development
-workflow, like ``uic``, ``rcc``, etc.
-
-All the tools **must** be used from the PySide wrappers, and not directly.
-For example, if exploring the ``site-packages/`` directory on your installation
-you find ``uic.exe`` (on Windows), you should not click on that, and use
-``pyside6-uic.exe`` instead.
-The reason for this is the proper setup of PATHs, plugins, and more,
-to properly work with the installed Python package.
-
-Here you can find all the tools we include in |project| starting
-from 6.3.0, grouped by different topics:
-
-Project development
-~~~~~~~~~~~~~~~~~~~
-
-* ``pyside6-project``, to build Qt Designer forms (``.ui`` files),
- resource files (``.qrc``) and QML type files (``.qmltype``) from
- a ``.pyproject`` file.
-
-Widget Development
-~~~~~~~~~~~~~~~~~~
-
-* ``pyside6-designer``, drag-and-drop tool for designing Widget UIs (generates ``.ui`` files,
- see :ref:`using_ui_files`).
-* ``pyside6-uic``, to generate Python code from ``.ui`` form files.
-* ``pyside6-rcc``, to generate serialized data from ``.qrc`` resources files.
- Keep in mind these files can be used in other non-widget projects.
-
-QML Development
-~~~~~~~~~~~~~~~
-
-* ``pyside6-qmllint``, that verifies the syntactic validity of QML files.
-* ``pyside6-qmltyperegistrar``, to read metatypes files and generate
- files that contain the necessary code to register all the types marked with
- relevant macros.
-* ``pyside6-qmlimportscanner``, to identify the QML modules imported from a
- project/QML files and dump the result as a JSON array.
-* ``pyside6-qmlcachegen``, to compile QML to bytecode at compile time for bundling inside the
- binary.
-
-Translations
-~~~~~~~~~~~~
-
-* ``pyside6-linguist``, for translating text in applications.
-* ``pyside6-lrelease``, to create run-time translation files for the application.
-* ``pyside6-lupdate``, to synchronize source code and translations.
-
-Qt Help
-~~~~~~~
-
-* ``pyside6-assistant``, for viewing online documentation in Qt Help file format.
- Read more about the formats on the `QtHelp Framework`_ page.
-
-.. _`QtHelp Framework`: https://doc.qt.io/qt-6/qthelp-framework.html
-
-PySide Utilities
-~~~~~~~~~~~~~~~~
-
-* ``pyside6-genpyi``, to generate Python stubs (``.pyi`` files) for Qt modules.
-* ``pyside6-metaobjectdump``, a tool to print out the metatype information in
- JSON to be used as input for ``qmltyperegistrar``.
-
-Deployment
-~~~~~~~~~~
-
-* ``pyside6-deploy``, to deploy PySide6 applications to desktop platforms -
- Linux, Windows and macOS.
-* ``pyside6-android-deploy``, to deploy PySide6 application as an Android app
- targeting different Android platforms - aarch64, armv7a, i686, x86_64.
+PySide6 comes bundled with a set of tools that assist in making the development experience with
+PySide6 more efficient. The list of tools can be found :ref:`here <package_tools>`.
* ``sphinx`` package for the documentation (optional).
* Check the platform dependencies of `Qt for Windows`_.
-.. note:: Python 3.8.0 was missing some API required for PySide/Shiboken so it's not possible
- to use it for a Windows build.
-
.. note:: The Python provided by the Microsoft Store is not compatible with PySide. Please
- use https://python.org/download to get a Python Interpreter.
+ use https://www.python.org/downloads/ to get a Python Interpreter.
.. _MSVC2022: https://visualstudio.microsoft.com/downloads/
.. _OpenSSL: https://sourceforge.net/projects/openssl/
PySide API reference.
+++
- .. button-ref:: api
+ .. button-ref:: pyside-api
:color: primary
:outline:
:expand:
:license: BSD, see LICENSE for details.
"""
-import sys
try:
from hashlib import md5
except ImportError:
from sphinx.ext.graphviz import render_dot_html, render_dot_latex
from inheritance_graph import InheritanceGraph
-from import_inheritance import (get_inheritance_entries_by_import,
- InheritanceException)
-from json_inheritance import (is_inheritance_from_json_enabled,
- get_inheritance_entries_from_json)
+from import_inheritance import (InheritanceException)
class inheritance_diagram(nodes.General, nodes.Element):
return md5(hashString.encode('utf-8')).hexdigest()[-10:]
-def fix_class_name(name):
- """Fix duplicated modules 'PySide6.QtCore.PySide6.QtCore.QObject'"""
- mod_pos = name.rfind('.PySide')
- return name[mod_pos + 1:] if mod_pos != -1 else name
-
-
-def expand_ref_uri(uri):
- """Fix a ref URI like 'QObject.html#PySide6.QtCore.PySide6.QtCore.QObject'
- to point from the image directory back to the HTML directory."""
- anchor_pos = uri.find('#')
- if anchor_pos == -1:
- return uri
- # Determine the path from the anchor "#PySide6.QtCore.PySide6.QtCore.QObject"
- class_name = fix_class_name(uri[anchor_pos + 1:])
- path = '../'
- modules = class_name.split('.')
- for m in range(min(2, len(modules))):
- path += f'{modules[m]}/'
- return path + uri[:anchor_pos] # Strip anchor
-
-
def html_visit_inheritance_diagram(self, node):
"""
Output the graph for HTML. This will insert a PNG with clickable
ref_title = child.get('reftitle')
uri = child.get('refuri')
if uri and ref_title:
- urls[fix_class_name(ref_title)] = expand_ref_uri(uri)
+ urls[ref_title] = uri
dotcode = graph.generate_dot(name, urls, env=self.builder.env)
render_dot_html(self, node, dotcode, {}, 'inheritance', 'inheritance',
import sys
-from import_inheritance import (get_inheritance_entries_by_import,
- InheritanceException)
+from import_inheritance import (get_inheritance_entries_by_import)
from json_inheritance import (is_inheritance_from_json_enabled,
get_inheritance_entries_from_json)
.. grid-item-card:: :mod:`QtDesigner <PySide6.QtDesigner>`
- Provides classes to extend Qt Designer.
+ Provides classes to extend *Qt Widgets Designer*.
.. grid-item-card:: :mod:`QtGui <PySide6.QtGui>`
Provides classes for setting up the controls from C++.
+ .. grid-item-card:: :mod:`QtQuickTest <PySide6.QtQuickTest>`
+
+ A unit test framework for QML applications where test cases are written as JavaScript functions.
+
.. grid-item-card:: :mod:`QtQuickWidgets <PySide6.QtQuickWidgets>`
Provides the QQuickWidget class for embedding Qt Quick in widget-based applications.
.. grid-item-card:: :mod:`QtUiTools <PySide6.QtUiTools>`
- Provides classes to handle forms created with Qt Designer.
+ Provides classes to handle forms created with *Qt Widgets Designer*.
.. grid-item-card:: :mod:`Qt WebChannel <PySide6.QtWebChannel>`
from docutils import nodes
from docutils.parsers.rst import Directive, directives
-from sphinx import addnodes
from sphinx.util import parselinenos
codec_info = codecs.lookup(encoding)
try:
f = codecs.StreamReaderWriter(open(fn, 'Ub'),
- codec_info[2], codec_info[3], 'strict')
+ codec_info[2], codec_info[3], 'strict')
lines = f.readlines()
f.close()
except (IOError, OSError):
lines = [lines[i] for i in linelist]
startafter = self.options.get('start-after')
- endbefore = self.options.get('end-before')
- prepend = self.options.get('prepend')
- append = self.options.get('append')
- snippet = self.options.get('snippet')
+ endbefore = self.options.get('end-before')
+ prepend = self.options.get('prepend')
+ append = self.options.get('append')
+ snippet = self.options.get('snippet')
if snippet:
startafter = "//![%s]" % snippet
parser.add_argument("--verbose", dest="verbose", action="store_true", default=False)
args = parser.parse_args()
+
+ core_index = Path(args.doc_data_dir) / "webxml" / "qtcore-index.webxml"
+ if core_index.is_file():
+ print(f"qdoc_spawner: {core_index} already exists, bailing out")
+ sys.exit(0)
+
files_prepare, files_single_exec = get_qdocconf_files()
parallel = args.parallel
import os
import json
import subprocess
-import sys
import warnings
from argparse import ArgumentParser, RawTextHelpFormatter
from pathlib import Path
def indent(lines, indent):
result = ''
- for l in lines:
- result = f"{result}{indent}{l}\n"
+ for line in lines:
+ result = f"{result}{indent}{line}\n"
return result
url = entry['Homepage']
version = entry['Version']
if url and version:
- content = f"{content}{rstUrl('Project Homepage', url)}, upstream version: {version}\n\n"
+ content = f"{content}{rstUrl('Project Homepage', url)}, upstream version: {version}\n\n" # noqa E:501
copyright = entry['Copyright']
if copyright:
content += rstLiteralBlockFromText(copyright)
--- /dev/null
+include(@QT_SRC_DIR@/../qtdeclarative/src/qmltest/doc/qtqmltest.qdocconf)
+includepaths += -I @QT_SRC_DIR@/../qtdeclarative/src/qmltest
+include(../pyside-config.qdocconf)
--- /dev/null
+.. _package_tools:
+
+Tools
+=====
+
+Following the same idea from the modules, we also include in the packages
+(wheels) Qt tools that are important for any Qt application development
+workflow, like ``uic``, ``rcc``, etc.
+
+All the tools **must** be used from the PySide wrappers, and not directly.
+For example, if exploring the ``site-packages/`` directory on your installation
+you find ``uic.exe`` (on Windows), you should not click on that, and use
+``pyside6-uic.exe`` instead.
+The reason for this is the proper setup of PATHs, plugins, and more,
+to properly work with the installed Python package.
+
+Here you can find all the tools we include in |project| starting
+from 6.3.0, grouped by different topics:
+
+Project development
+~~~~~~~~~~~~~~~~~~~
+
+.. grid:: 2
+ :gutter: 3 3 4 5
+
+ .. grid-item-card:: ``pyside6-project``
+ :link: pyside6-project
+ :link-type: ref
+
+ to build *Qt Widgets Designer* forms (``.ui`` files),
+ resource files (``.qrc``) and QML type files (``.qmltype``)
+ from a ``.pyproject`` file.
+
+Widget Development
+~~~~~~~~~~~~~~~~~~
+
+.. grid:: 2
+ :gutter: 3 3 4 5
+
+ .. grid-item-card:: ``pyside6-designer``
+ :link: pyside6-designer
+ :link-type: ref
+
+ drag-and-drop tool for designing Widget UIs (generates ``.ui`` files,
+ see :ref:`using_ui_files`).
+
+ .. grid-item-card:: ``pyside6-uic``
+ :link: pyside6-uic
+ :link-type: ref
+
+ to generate Python code from ``.ui`` form files.
+
+ .. grid-item-card:: ``pyside6-rcc``
+ :link: pyside6-rcc
+ :link-type: ref
+
+ to generate serialized data from ``.qrc`` resources files.
+ Keep in mind these files can be used in other non-widget projects.
+
+
+QML Development
+~~~~~~~~~~~~~~~
+
+.. grid:: 2
+ :gutter: 3 3 4 5
+
+ .. grid-item-card:: ``pyside6-qmllint``
+ :link: pyside6-qmllint
+ :link-type: ref
+
+ that verifies the syntactic validity of QML files.
+
+ .. grid-item-card:: ``pyside6-qmltyperegistrar``
+ :link: pyside6-qmltyperegistrar
+ :link-type: ref
+
+ to read metatypes files and generate files that contain the necessary
+ code to register all the types marked with relevant macros.
+
+ .. grid-item-card:: ``pyside6-qmlimportscanner``
+ :link: pyside6-qmlimportscanner
+ :link-type: ref
+
+ to identify the QML modules imported from a
+ project/QML files and dump the result as a JSON array.
+
+ .. grid-item-card:: ``pyside6-qmlcachegen``
+ :link: pyside6-qmlcachegen
+ :link-type: ref
+
+ to compile QML to bytecode at compile time for bundling inside the
+ binary.
+
+ .. grid-item-card:: ``pyside6-qml``
+ :link: pyside6-qml
+ :link-type: ref
+
+ to enable quick prototyping with QML files. This tool mimics some of
+ the capabilities of Qt's ``QML`` runtime utility by
+ directly invoking QQmlEngine/QQuickView.
+
+Translations
+~~~~~~~~~~~~
+
+.. grid:: 2
+ :gutter: 3 3 4 5
+
+ .. grid-item-card:: ``pyside6-linguist``
+ :link: pyside6-linguist
+ :link-type: ref
+
+ for translating text in applications.
+
+ .. grid-item-card:: ``pyside6-lrelease``
+ :link: pyside6-lrelease
+ :link-type: ref
+
+ to create run-time translation files for the application.
+
+ .. grid-item-card:: ``pyside6-lupdate``
+ :link: pyside6-lupdate
+ :link-type: ref
+
+ to synchronize source code and translations.
+
+Qt Help
+~~~~~~~
+
+.. grid:: 2
+ :gutter: 3 3 4 5
+
+ .. grid-item-card:: ``pyside6-assistant``
+ :link: pyside6-assistant
+ :link-type: ref
+
+ for viewing online documentation in Qt Help file format.
+ Read more about the formats on the `QtHelp Framework`_ page.
+
+.. _`QtHelp Framework`: https://doc.qt.io/qt-6/qthelp-framework.html
+
+PySide Utilities
+~~~~~~~~~~~~~~~~
+
+.. grid:: 2
+ :gutter: 3 3 4 5
+
+ .. grid-item-card:: ``pyside6-genpyi``
+ :link: pyside6-genpyi
+ :link-type: ref
+
+ to generate Python stubs (``.pyi`` files) for Qt modules.
+
+ .. grid-item-card:: ``pyside6-metaobjectdump``
+ :link: pyside6-metaobjectdump
+ :link-type: ref
+
+ a tool to print out the metatype information in JSON to be used as
+ input for ``qmltyperegistrar``.
+
+Deployment
+~~~~~~~~~~
+
+.. grid:: 2
+ :gutter: 3 3 4 5
+
+ .. grid-item-card:: ``pyside6-deploy``
+ :link: pyside6-deploy
+ :link-type: ref
+
+ to deploy PySide6 applications to desktop platforms - Linux, Windows
+ and macOS.
+
+ .. grid-item-card:: ``pyside6-android-deploy``
+ :link: pyside6-android-deploy
+ :link-type: ref
+
+ to deploy PySide6 application as an Android app targeting different
+ Android platforms - aarch64, armv7a, i686, x86_64.
+
+Shader Tools
+~~~~~~~~~~~~
+
+.. grid:: 2
+ :gutter: 3 3 4 5
+
+ .. grid-item-card:: ``pyside6-qsb``
+ :link: pyside6-qsb
+ :link-type: ref
+
+ a command-line tool provided by the Qt Shader Tools modules to
+ generate and inspect .qsb files.
+
+Qt Quick 3D
+~~~~~~~~~~~
+
+.. grid:: 2
+ :gutter: 3 3 4 5
+
+ .. grid-item-card:: ``pyside6-balsam``
+ :link: pyside6-balsam
+ :link-type: ref
+
+ a command line tool that takes assets created in digital content
+ creation tools like Maya, 3ds Max or Blender and converts them into an
+ efficient runtime format for use with Qt Quick 3D.
+
+ .. grid-item-card:: ``pyside6-balsamui``
+ :link: pyside6-balsamui
+ :link-type: ref
+
+ a graphical user interface for the ``pyside6-balsam`` tool.
--- /dev/null
+.. _pyside6-assistant:
+
+pyside6-assistant
+=================
+
+``pyside6-assistant`` is a tool that wraps `Qt Assistant`_, the help
+viewer of Qt for use with the Qt help file format (see `The Qt Help Framework`_).
+
+The version of assistant shipped with Qt for Python does not contain
+any documentation.
+
+You can build the Qt for Python documentation in the Qt help file format
+(see :ref:`building_documentation`) and register it for use in `Preferences`
+dialog of ``pyside6-assistant`` (`Edit/Preferences`).
+
+.. image:: pyside6-assistant_screenshot.webp
+ :width: 381
+ :alt: PySide6 Assistant Screenshot
+
+.. _`Qt Assistant`: https://doc.qt.io/qt-6/qtassistant-index.html
+.. _`The Qt Help Framework`: https://doc.qt.io/qt-6/qthelp-framework.html
--- /dev/null
+.. _pyside6-designer:
+
+pyside6-designer
+================
+
+``pyside6-designer`` is a tool that wraps the `Qt Widgets Designer`_,
+to enable you to design Qt Widgets applications with a *drag-and-drop*
+approach.
+
+.. image:: pyside6-designer_screenshot.webp
+ :width: 100%
+ :alt: PySide6 Designer Screenshot
+
+Usage
+-----
+
+With ``pyside6-designer`` you can design your application in a simple way,
+to later save the end result in a ``.ui`` file. When you start the tool, you
+will see a dialog to select the base window: a QWidget, a QMainWindow, etc.
+
+.. image:: pyside6-designer_base_screenshot.webp
+ :width: 50%
+ :alt: PySide6 Designer Initial Screenshot
+
+Once you select one of those options, you can start placing widgets
+into the interface, and have access to the whole structure, properties,
+and more.
+
+.. image:: pyside6-designer_sections_screenshot.webp
+ :width: 100%
+ :alt: PySide6 Designer Initial Screenshot
+
+A simple distinction of the areas you might use is described in the
+previous screenshot. In the section **1** you will find all the elements
+you can use in your application, which is the area **2**.
+The application designs follows a hierarchical configuration,
+in **3** you can see the structure of the example which contains
+only a ``QMainWindow`` and a ``QPushButton``. Lastly
+you can access and modify the properties of the item in **4**,
+where you could adjust dimensions, names, etc.
+
+
+When your application is finished, you will save your design in a ``.ui``
+file. This ``.ui`` file can later be converted into a Python file,
+with the help of the :ref:`pyside6-uic` tool. You can find
+more details of this process in: :ref:`using_ui_files`.
+
+If you are interested in more functionality of the tool, you can check
+the official `Qt Widgets Designer Manual`_.
+
+
+Custom Widgets
+--------------
+
+One of the features that the `Qt Widgets Designer`_ provides is the possibility
+of loading custom widgets, in order to facilitate the development with ad-hoc
+widgets.
+
+On the following screenshot, you can see a new component on the left column
+that is already added on the main widget, a tic-tac-toe custom widget.
+
+.. image:: pyside6-designer_customwidgets_screenshot.webp
+ :width: 100%
+ :alt: PySide6 Designer Custom Widgets Screenshot
+
+To achieve this, you need to register a custom widget by setting the environment
+variable ``PYSIDE_DESIGNER_PLUGINS`` to the directory where your register file
+is located. The registration file for the tic-tac-toe widget looks like this:
+
+.. code-block:: Python
+
+ from tictactoe import TicTacToe
+ from tictactoeplugin import TicTacToePlugin
+
+ from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection
+
+
+ if __name__ == '__main__':
+ QPyDesignerCustomWidgetCollection.addCustomWidget(TicTacToePlugin())
+
+As you can see in the previous code, ``TicTacToe`` is the custom widget,
+imported from a different file, and the ``TicTacToePlugin`` is the interface
+layer for `Qt Widgets Designer`_ to recognize it.
+We provide a helper class, ``QPyDesignerCustomWidgetCollection``, to
+simplify the registration process.
+
+If you are interested in all the details of this specific case, you can
+check the :ref:`task-menu-extension-example`.
+
+.. _`Qt Widgets Designer`: https://doc.qt.io/qt-6/qtdesigner-manual.html
+.. _`Qt Widgets Designer Manual`: https://doc.qt.io/qt-6/qtdesigner-manual.html
--- /dev/null
+.. _pyside6-genpyi:
+
+pyside6-genpyi
+==============
+
+`pyside6-genpyi` is a command line tool to generate Python stub files
+(.pyi) for PySide modules. Stub files define signatures of all classes,
+methods (including overloads), constants and enums of the PySide
+modules. Signatures also contain type hints. This helps PySide integrate
+with Python type checkers and IDEs. For example, if you use any function
+from the Qt API with PySide, your IDE's function lookup feature will
+show you the function signature and its parameters and return value
+including types.
+
+PySide6 already ships with stub files that were generated with
+`pyside6-genpyi`. However, if you want to generate new stub files for
+several (or all) modules, for example to toggle a few features, you can
+run `pyside6-genpyi` manually. If you want to generate stub files for
+your own custom module, refer to :ref:`shiboken6-genpyi`.
+
+
+Usage
+-----
+
+To generate stub files for a PySide module, run the following command:
+
+.. code-block:: bash
+
+ pyside6-genpyi <module_names> [OPTIONS]
+
+where `<module_names>` is a space-separated list of module names (the
+modules must be importable from the working directory) and where
+`[OPTIONS]` can be one of the following:
+
+* **--quiet**: Run the tool quietly without output to stdout.
+* **--outpath <output_dir>**: Specify the output directory for the
+ generated stub files. If not specified, the stub files are generated
+ in the location of the module binary.
+* **--sys-path <paths>**: Prepend the system path (`sys.path`) with a
+ space-separated list of strings `<paths>`. This is useful if the
+ module is not installed in a default lookup location.
+* **--feature <features>**: A space-separate list of optional PySide
+ features to enable (see :ref:`pysideapi2`). This option has no effect
+ when using PyPy. Currently, the following features are available:
+
+ * **snake_case**: All methods in the module are switched from
+ ``camelCase`` to ``snake_case``. A single upper case letter is
+ replaced by an underscore and the lower case letter.
+ * **true_property**: All getter and setter functions in the module
+ which are marked as a property in the Qt6 docs are replaced by Python
+ property objects. Properties are also listed as such in the according
+ QMetaObject of a class.
--- /dev/null
+.. _pyside6-linguist:
+
+pyside6-linguist
+=================
+
+``pyside6-linguist`` is a tool that wraps `Qt Linguist`_, Qt's tool to
+translate user interfaces and manage application localizations. It
+supports Qt's own TS file format as well as the XML Localization
+Interchange File Format (XLIFF). There are no differences between the
+version bundled with PySide and the one from Qt.
+
+For more information on how to use this tool, read Qt's documentation
+here: `Qt Linguist`_. Read more about how to translate PySide
+applications here: :ref:`translations`.
+
+.. image:: pyside6-linguist_screenshot.webp
+ :width: 500
+ :alt: PySide6 Linguist Screenshot
+
+.. _`Qt Linguist`: https://doc.qt.io/qt-6/qtlinguist-index.html
--- /dev/null
+.. _pyside6-lrelease:
+
+pyside6-lrelease
+================
+
+.. note:: This tool is automatically called by :ref:`pyside6-project`
+ so you don't need to call it manually. *Qt Creator* will take care
+ of this step as well while executing a project.
+
+``pyside6-lrelease`` is a command line tool wrapping `lrelease`_. It produces
+``.qm`` files out of ``.ts`` files. The ``.qm`` file format is a compact binary
+format that the localized application uses. It provides extremely fast lookup
+for translations (see :ref:`translations`).
+
+Usage
+-----
+
+To convert a ``.ts`` file of the :ref:`qt-linguist-example`
+into its binary representation, run:
+
+.. code-block:: bash
+
+ pyside6-lrelease example_de.ts -qm example_de.qm
+
+.. _`lrelease`: https://doc.qt.io/qt-6/linguist-lrelease.html
--- /dev/null
+.. _pyside6-lupdate:
+
+pyside6-lupdate
+===============
+
+.. note:: This tool is automatically called by :ref:`pyside6-project`
+ so you don't need to call it manually.
+
+``pyside6-lupdate`` is a command line tool wrapping `lupdate`_. It finds
+translatable strings in Python, ``.ui``, and ``.qml`` files and generates or
+updates ``.ts`` files (see :ref:`translations`).
+
+Usage
+-----
+
+To create or update the ``.ts`` file of the :ref:`qt-linguist-example`,
+run:
+
+.. code-block:: bash
+
+ pyside6-lupdate main.py main.qml form.ui -ts example_de.ts
+
+.. _`lupdate`: https://doc.qt.io/qt-6/linguist-lupdate.html
--- /dev/null
+.. _pyside6-metaobjectdump:
+
+pyside6-metaobjectdump
+======================
+
+``pyside6-metaobjectdump`` is a command line tool. It scans Python source
+files and dumps out information on classes to be registered with QML in
+JSON-format. This serves as input for the :ref:`pyside6-qmltyperegistrar` tool.
+
+The tool is the equivalent of the `moc`_ tool in Qt / C++.
+
+It is automatically run by the :ref:`pyside6-project` tool
+when passing the ``qmllint`` argument instructing it to check
+the QML source files.
+
+Usage
+-----
+
+Classes to be registered with QML are indicated by QML decorators
+like :deco:`QmlElement`. Invoking:
+
+.. code-block:: bash
+
+ pyside6-metaobjectdump birthdayparty.py
+
+produces the JSON data on stdout:
+
+.. code-block:: json
+
+ [
+ {
+ "classes": [
+ {
+ "className": "BirthdayParty",
+ "qualifiedClassName": "BirthdayParty",
+ "object": true,
+ "superClasses": [
+ {
+ "access": "public",
+ "name": "QObject"
+ }
+ ],
+ "classInfos": [
+ {
+ "name": "QML.Element",
+ "value": "auto"
+ }
+ ],
+ "properties": [
+ {
+ "name": "host",
+ "type": "Person",
+ "index": 0,
+ "read": "host",
+ "notify": "host_changed",
+ "write": "host"
+ },
+ {
+ "name": "guests",
+ "type": "QQmlListProperty<Person>",
+ "index": 1
+ }
+ ],
+ "signals": [
+ {
+ "access": "public",
+ "name": "host_changed",
+ "arguments": [],
+ "returnType": "void"
+ },
+ {
+ "access": "public",
+ "name": "guests_changed",
+ "arguments": [],
+ "returnType": "void"
+ }
+ ]
+ }
+ ],
+ "outputRevision": 68,
+ "QML_IMPORT_NAME": "People",
+ "QML_IMPORT_MAJOR_VERSION": 1,
+ "QML_IMPORT_MINOR_VERSION": 0,
+ "QT_MODULES": [
+ "QtCore",
+ "QtQml"
+ ],
+ "inputFile": ".../examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/birthdayparty.py"
+ }
+ ]
+
+.. _`moc`: https://doc.qt.io/qt-6/moc.html
--- /dev/null
+.. _pyside6-project:
+
+pyside6-project
+===============
+
+`pyside6-project` is a command line tool for creating, building and deploying
+|project| applications. It operates on a project file which is also used by
+`Qt Creator`_.
+
+Project file format
+-------------------
+
+The project file format is a simple `JSON`_-based format with the suffix
+``.pyproject`` listing all files of the project excluding generated files
+(typically ``.py``, ``.qml``, ``.qrc``, ``.ts``, or ``.ui`` files):
+
+.. code-block:: json
+
+ {
+ "files": ["main.py"]
+ }
+
+
+Usage
+-----
+
+The tool has several subcommands. New projects can be created using
+the below commands, passing the project name (directory):
+
+*new-ui*
+ Creates a new QtWidgets project with a *Qt Widgets Designer*-based main
+ window.
+
+*new-widget*
+ Creates a new QtWidgets project with a main window.
+
+*new-quick*
+ Creates a new QtQuick project.
+
+The other commands take the project file as an argument.
+It is also possible to specify a directory containing the project file.
+
+*build*
+ Builds the project, generating the required build artifacts
+ (see :ref:`using_ui_files`, :ref:`using_qrc_files`).
+
+*run*
+ Builds the project and runs the main.
+
+*deploy*
+ Deploys the application (see see :ref:`pyside6-deploy`).
+
+*lupdate*
+ Updates translation (.ts) files (see :ref:`translations`).
+
+*clean*
+ Cleans the build artifacts.
+
+*qmllint*
+ Runs the ``qmllint`` tool, checking the QML files.
+
+
+.. _`Qt Creator`: https://www.qt.io/product/development-tools
+.. _`JSON`: https://www.json.org/
--- /dev/null
+.. _pyside6-qml:
+
+pyside6-qml
+===========
+
+``pyside6-qml`` mimics some capabilities of Qt's `qml <qml_runtime>`_ runtime utility by directly
+invoking QQmlEngine/QQuickView. It enables prototyping with QML/QtQuick without the need to write
+any Python code that loads the QML files either through `QQmlApplicationEngine <qqmlappengine>`_ or
+the `QQuickView <qquickview>`_ class. The tool also detects the QML classes implemented in Python
+and registers them with the QML type system.
+
+Usage
+-----
+
+Consider the example `Extending QML - Plugins Example <extending_qml_example>`_. This example does
+not have a Python file with a ``main`` function that initializes a QmlEngine to load the QML file
+``app.qml``. You can run the example by running
+
+.. code-block:: bash
+
+ pyside6-qml examples/qml/tutorials/extending-qml/chapter6-plugins/app.qml -I examples/qml/tutorials/extending-qml/chapter6-plugins/Charts
+
+The ``-I`` flag is used to point ``pyside6-qml`` to the folder containing Python files that
+implement QML classes.
+
+Command Line Options
+--------------------
+
+Here are all the command line options of ``pyside6-qml``:
+
+Arguments
+^^^^^^^^^
+
+* **file**: This option refers to the QML file to be loaded by ``pyside6-qml``. This option does not
+ have a name or a flag. Therefore, this option should be the first option supplied to
+ ``pyside6-qml``. For example,
+
+.. code-block:: bash
+
+ pyside6-qml /path/to/test.qml
+
+Options
+^^^^^^^
+
+* **--module-paths/-I**: Specify space-separated folder/file paths which point to the Python files
+ that implement QML classes. By default, the parent directory of the QML file supplied to
+ ``pyside6-qml`` is searched recursively for all Python files and they are imported. Otherwise,
+ only the paths given in module paths are searched.
+
+* **--verbose/-v**: Run ``pyside6-qml`` in verbose mode. When run in this mode, pyside6-qml prints
+ log messages during various stages of processing.
+
+Options that align with `QML <qml_runtime>`_ runtime utility
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* **--app-typ/-a**: Specifies which application class to use. It takes one of the three values -
+ ``core, gui, widget``. The default value is *gui*.
+
+* **--config/-c**: Load the given built-in configuration. It takes one of two values - ``default,
+ resizeToItem``. This option is only relevant for a QtQuick application. If ``default`` is used,
+ the view resizes to the size of the root item in the QML. If ``resizeToItem`` is used, the view
+ automatically resizes the root item to the size of the view.
+
+* **--list-conf**: List the built-in configurations. ``pyside6-qml`` has two built-in configurations
+ - ``default`` and ``resizeToItem``. See the option ``--config`` for more information.
+
+* **--rhi/-r**: Specifies the backend for the Qt graphics abstraction (RHI). It takes one of the
+ four values - ``vulkan, metal, d3dll, gl``.
+
+* **--verbose/-v**: List the built-in configurations. ``pyside6-qml`` has two built-in
+ configurations - *default* and *resizeToItem*. See the option ``--config`` for more information.
+
+* **--gles**: Force use of GLES (AA_UseOpenGLES).
+
+* **--desktop**: Force use of desktop OpenGL (AA_UseDesktopOpenGL).
+
+* **--software**: Force use of software rendering(AA_UseSoftwareOpenGL).
+
+* **--disable-context-sharing**: Disable the use of a shared GL context for QtQuick Windows".
+
+.. _`qml_runtime`: https://doc.qt.io/qt-6/qtquick-qml-runtime.html
+.. _`qqmlappengine`: https://doc.qt.io/qt-6/qqmlapplicationengine.html
+.. _`qquickview`: https://doc.qt.io/qt-6/qquickview.html
+.. _`extending_qml_example`: https://doc.qt.io/qtforpython-6/examples/example_qml_tutorials_extending-qml_chapter6-plugins.html
--- /dev/null
+.. _pyside6-qmlcachegen:
+
+pyside6-qmlcachegen
+===================
+
+``pyside6-qmlcachegen`` is a command line tool that wraps `qmlcachegen`_.
+This tool creates C++ code or `QML byte code` for ``.qml`` files. For
+Qt for Python, only `QML byte code` is relevant. The file suffix is
+``.qmlc`` and it works similar to compiled Python bytecode
+(``.pyc`` files).
+
+Usage
+-----
+
+The command line option ``--only-bytecode`` should be used to
+create `QML byte code`. For example:
+
+.. code-block:: bash
+
+ qmlcachegen --only-bytecode gallery.qml
+
+produces a file ``gallery.qmlc`` containing `QML byte code` which is
+automatically loaded by the QML engine.
+
+.. _`qmlcachegen`: https://doc.qt.io/qt-6/qtqml-tool-qmlcachegen.html
--- /dev/null
+.. _pyside6-qmllint:
+
+pyside6-qmllint
+===============
+
+``pyside6-qmllint`` is a command line tool that wraps `qmllint`_. This tool
+verifies the syntatic validity of QML files and warns about some QML
+anti-patterns.
+
+It is automatically run by the :ref:`pyside6-project` tool
+when passing the ``qmllint`` argument instructing it to check
+the QML source files.
+
+Usage
+-----
+
+The tool should normally not be invoked manually since it requires
+a number of import paths and additional type information
+generated by :ref:`pyside6-qmltyperegistrar` to function.
+
+For example, for a ``.qml`` file like:
+
+.. code-block:: javascript
+
+ import QtQuick
+ import QtQuick.Controls
+
+ Item {
+ Text {
+ id: name
+ text: qsTr("Hello World")
+ }
+ }
+
+when running:
+
+.. code-block:: bash
+
+ pyside6-qmllint Main.qml
+
+it would warn about unused imports:
+
+.. code-block::
+
+ Info: Main.qml:2:1: Unused import [unused-imports]
+ import QtQuick.Controls
+ ^^^^^^
+
+.. _`qmllint`: https://doc.qt.io/qt-6/qtquick-tool-qmllint.html
--- /dev/null
+.. _pyside6-qmltyperegistrar:
+
+pyside6-qmltyperegistrar
+========================
+
+``pyside6-qmltyperegistrar`` is a command line tool that wraps the
+``qmltyperegistrar`` tool of Qt.
+
+It takes the file produced by :ref:`pyside6-metaobjectdump`
+as input and generates C++ code to register C++ classes to QML
+and a ``.qmltypes`` file containing a JSON description of the
+classes. For Qt for Python, only the ``.qmltypes`` file
+is of interest as input for :ref:`pyside6-qmllint`.
+
+The tool is automatically run by the :ref:`pyside6-project` tool
+when passing the ``qmllint`` argument instructing it to check
+the QML source files.
--- /dev/null
+.. _pyside6-rcc:
+
+pyside6-rcc
+===========
+
+.. note:: This tool is automatically called by :ref:`pyside6-project`
+ so you don't need to call it manually. *Qt Creator* will take care
+ of this step as well while executing a project.
+
+
+``pyside6-rcc`` is a command line tool for converting ``.qrc`` files into ``.py``
+files, so they can be used within your Python code.
+
+The tool is a wrapper around the `rcc`_ tool, which was originally
+designed to generate C++ code, but it also has Python support.
+
+Even though the equivalent of ``pyside6-rcc`` is running ``rcc -g python``
+we strongly recommend you to rely on ``pyside6-rcc`` in order to avoid
+mismatches between versions for the generated code.
+
+Usage
+-----
+
+Once you have gathered your resources on a qrc file,
+you can transform your ``.qrc`` file with the following command:
+
+.. code-block:: bash
+
+ pyside6-rcc your_file.qrc -o rc_your_file.py
+
+It is important to use the ``-o`` option to generate the Python file with the
+conversion, otherwise you will receive all the output as stdout in your terminal.
+
+To enable the usage of those resources in your program, you need to import
+the file:
+
+.. code-block:: Python
+
+ import rc_your_file
+
+then you can use a specific resource, for example an image, with the prefix ``:/``,
+for example:
+
+.. code-block:: Python
+
+ pixmap = QPixmap(":/icons/image.png")
+
+
+For additional options, you can use ``pyside6-rcc -h`` in order to get
+more information about additional options.
+
+Visit the tutorial :ref:`using_qrc_files` for a hands-on example.
+
+.. _`rcc`: https://doc.qt.io/qt-6/rcc.html
+
--- /dev/null
+.. _pyside6-uic:
+
+pyside6-uic
+===========
+
+.. note:: This tool is automatically called by :ref:`pyside6-project`
+ so you don't need to call it manually. *Qt Creator* will take care
+ of this step as well while executing a project.
+
+``pyside6-uic`` is a command line tool for converting ``.ui`` files into ``.py``
+files, with the objective of using application designs as Python classes.
+
+The tool is a wrapper around the `uic`_ tool, which was originally
+designed to generate C++ code, but it also has Python support.
+
+Even though the equivalent of ``pyside6-uic`` is running ``uic -g python``
+we strongly recommend you to rely on ``pyside6-uic`` in order to avoid
+mismatches between versions for the generated code.
+
+Usage
+-----
+
+Once you have designed your application with :ref:`pyside6-designer`,
+you can transform your ``.ui`` file with the following command:
+
+.. code-block:: bash
+
+ pyside6-uic your_file.ui -o ui_your_file.py
+
+It is important to use the ``-o`` option to generate the Python file with the
+conversion, otherwise you will receive all the output as stdout in your terminal.
+
+The structure of the generated Python file will be similar in all cases,
+and you will get one class called ``Ui_TheNameOfYourDesign(object)`` that
+is in charge of positioning all the elements like your design.
+
+To use this Python file, you should follow our tutorial in
+:ref:`using_ui_files`, but in summary, it is mainly importing the class
+from the generated file and setting it up in your code:
+
+.. code-block:: Python
+
+ self.ui = Ui_TheNameOfYourDesign()
+ self.ui.setupUi(self)
+
+For additional options, you can use ``pyside-uic -h`` in order to get
+more information related to relative imports, absolute imports, using resources,
+translations, etc.
+
+.. note:: Remember that you need to have a class corresponding to the base
+ form you selected in :ref:`pyside6-designer`, a ``QWidget``, or ``QDialog``,
+ or ``QMainWindow``, etc, in order for ``setupUi`` to work. Check
+ :ref:`using_ui_files` for more information.
+
+.. warning:: Do not modify the content of the generated Python file from your
+ ``.ui`` file, otherwise everything will be lost when you re-generate it.
+
+.. _`uic`: https://doc.qt.io/qt-6/uic.html
+
--- /dev/null
+.. _pyside6-balsam:
+
+pyside6-balsam
+==============
+
+``pyside6-qsb`` is a tool that wraps the `balsam <Balsam Asset Import Tool>`_
+tool provided with Qt Quick 3D. The Balsam tool is a command line application
+that is part of Qt Quick 3D's asset conditioning pipeline. The purpose is to
+take assets created in digital content creation tools like `Maya`_, `3ds Max`_
+or `Blender`_ and converts them into an efficient runtime format for use with Qt
+Quick 3D. It is not possible, nor does it make sense to reference the
+interchange formats directly in applications because a large amount of
+resources are needed to parse and condition the content of the asset before it
+is usable for real-time rendering. Instead, the interchange formats can be
+converted via the Balsam tool into QML Components and resources like geometry
+and textures.
+
+
+For more information on how to use this tool, read Qt's documentation
+here: `Balsam Asset Import Tool`_.
+
+Usage
+-----
+
+.. code-block:: bash
+
+ pyside6-balsam [options] sourceFileName
+
+To convert a 3D asset contained in the file ``testModel.fbx`` with
+``pyside6-balsam`` the following command would be used:
+
+.. code-block:: bash
+
+ pyside6-balsam testModel.fbx
+
+This would generate the following files:
+
+* meshes/testModel.mesh
+* TestModel.qml
+
+Which can then be used in a Qt Quick 3D project by using that QML Component:
+
+.. code-block:: xml
+
+ import QtQuick3D 1.0
+
+ Scene {
+ Model {
+ source: "TestModel.qml"
+ }
+ }
+
+For other modes of operation, refer to the `Balsam Asset Import Tool`_.
+
+.. _`Balsam Asset Import Tool`: https://doc.qt.io/qt-6/qtquick3d-tool-balsam.html
+.. _Maya: https://www.autodesk.com/products/maya/overview
+.. _3ds Max: https://www.autodesk.com/products/3ds-max/overview
+.. _Blender: https://www.blender.org/
+
--- /dev/null
+.. _pyside6-balsamui:
+
+pyside6-balsamui
+================
+
+``pyside6-balsamui`` is graphical user interface frontend to the command line
+tool :ref:`pyside6-balsam`. The purpose of the tool is to take assets created
+in digital content creation tools like `Maya`_, `3ds Max`_ or `Blender`_ and
+converts them into an efficient runtime format for use with Qt Quick 3D.
+
+For more information on the further capabilities of the tool, read Qt's
+documentation here: `Balsam Asset Import Tool`_.
+
+.. image:: pyside6-balsamui_screenshot.webp
+ :width: 500
+ :alt: pyside6-balsamui screenshot
+
+.. _`Balsam Asset Import Tool`: https://doc.qt.io/qt-6/qtquick3d-tool-balsam.html
+.. _Maya: https://www.autodesk.com/products/maya/overview
+.. _3ds Max: https://www.autodesk.com/products/3ds-max/overview
+.. _Blender: https://www.blender.org/
+
--- /dev/null
+.. _pyside6-qmlimportscanner:
+
+pyside6-qmlimportscanner
+========================
+
+``pyside6-qmlimportscanner`` is a command line tool that wraps the
+``qmlimportscanner`` tool of Qt.
+
+
+The tool is automatically run by the :ref:`pyside6-project` tool
+when passing the ``qmllint`` argument instructing it to check
+the QML source files.
+
+Usage
+-----
+
+Invoking the tool in the directory of the :ref:`filesystemexplorer_example`
+example using:
+
+.. code-block:: bash
+
+ pyside6-qmlimportscanner -rootPath .
+
+produces:
+
+.. code-block:: json
+
+ [
+ {
+ "name": "QtQuick",
+ "type": "module"
+ },
+ {
+ "name": "QtQuick.Controls.Basic",
+ "type": "module"
+ },
+ {
+ "name": "QtQuick.Layouts",
+ "type": "module"
+ },
+ {
+ "name": "FileSystemModule",
+ "type": "module"
+ },
+ {
+ "name": "QtQuick.Controls",
+ "type": "module"
+ },
+ {
+ "name": "QtQuick.Effects",
+ "type": "module"
+ }
+ ]
--- /dev/null
+.. _pyside6-qsb:
+
+pyside6-qsb
+===========
+
+``pyside6-qsb`` is a tool that wraps the `qsb <QSB Manual>`_ tool. qsb is a
+command line tool provided by the `Qt Shader Tools`_ module. It integrates
+third-party libraries such as `glslang`_ and `SPIRV-Cross`_, optionally invokes
+external tools, such as ``fxc`` or ``spirv-opt``, and generates .qsb files.
+Additionally, it can be used to inspect the contents of a .qsb package.
+
+For more information on how to use this tool, read Qt's documentation
+here: `QSB Manual`_.
+
+Usage
+-----
+
+To create a qsb file from a shader file, e.g., ``shader.frag``, use the
+following command:
+
+.. code-block:: bash
+
+ pyside6-qsb -o shader.frag.qsb shader.frag
+
+To inspect the file produced, i.e., ``shader.frag.qsb``, use the following
+command:
+
+.. code-block:: bash
+
+ pyside6-qsb -d shader.frag.qsb
+
+This will print the reflection metadata (in JSON form) and the included shaders.
+
+For other modes of operation, refer to the `QSB Manual`_.
+
+.. _`glslang`: https://github.com/KhronosGroup/glslang
+.. _`spirv-cross`: https://github.com/KhronosGroup/SPIRV-Cross
+.. _`QSB Manual`: https://doc.qt.io/qt-6/qtshadertools-qsb.html
+.. _`Qt Shader Tools`: https://doc.qt.io/qt-6/qtshadertools-index.html
::
- </ui>
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>icons/play.png</file>
Connections can be spelled out in code or, for widget forms,
designed in the
`Signal-Slot Editor <https://doc.qt.io/qt-6/designer-connection-mode.html>`_
-of Qt Designer.
+of *Qt Widgets Designer*.
The Signal Class
----------------
// do something with 'sum'
}
+
+.. _slot-decorator:
+
The Slot Class
--------------
someone.speak[str].emit("Hello everybody!")
+.. _signals-and-slots-strings:
+
Specifying Signals and Slots by Method Signature Strings
--------------------------------------------------------
from PySide6.QtCore import SIGNAL, SLOT
button.connect(SIGNAL("clicked(Qt::MouseButton)"),
- action_handler, SLOT("action1(Qt::MouseButton)"))
+ action_handler, SLOT("action1(Qt::MouseButton)"))
-This is not recommended for connecting signals, it is mostly
-used to specify signals for methods like ``QWizardPage::registerField()``:
+This is not normally recommended; it is only needed
+for a few cases where signals are only accessible via ``QMetaObject``
+(``QAxObject``, ``QAxWidget``, ``QDBusInterface`` or ``QWizardPage::registerField()``):
.. code-block:: python
wizard.registerField("text", line_edit, "text",
SIGNAL("textChanged(QString)"))
+
+The signature strings can be found by querying ``QMetaMethod.methodSignature()``
+when introspecting ``QMetaObject``:
+
+.. code-block:: python
+
+ mo = widget.metaObject()
+ for m in range(mo.methodOffset(), mo.methodCount()):
+ print(mo.method(m).methodSignature())
+
+Slots should be decorated using :ref:`@Slot <slot-decorator>`.
The source files generated by ``pyside6-uic`` from the form files
should **not** be passed.
+The ``lupdate`` mode of ``pyside6-project`` can also be used for this. It
+collects all source files and runs ``pyside6-lupdate`` when ``.ts`` file(s)
+are given in the ``.pyproject`` file:
+
+.. code-block:: bash
+
+ pyside6-project lupdate .
+
``.ts`` files are translated using *Qt Linguist*. Once this is complete,
the files are converted to a binary form (``.qm`` files):
.. code-block:: bash
- mkdir translations
- pyside6-lrelease example_de.ts -qm translations/example_de.qm
+ pyside6-lrelease example_de.ts -qm example_de.qm
+
+``pyside6-project`` will build the ``.qm`` file automatically when
+``.ts`` file(s) are given in the ``.pyproject`` file:
+
+.. code-block:: bash
+
+ pyside6-project build .
To avoid having to ship the ``.qm`` files, it is recommend
to put them into a Qt resource file along with icons and other
.. code-block:: xml
<!DOCTYPE RCC><RCC version="1.0">
- <qresource>
- <file>translations/example_de.qm</file>
+ <qresource prefix="translations">
+ <file>example_de.qm</file>
</qresource>
</RCC>
*************************************************************************************
This page describes the use of
-`Qt Designer <https://doc.qt.io/qt-6/qtdesigner-manual.html>`_ to create
+`Qt Widgets Designer <https://doc.qt.io/qt-6/qtdesigner-manual.html>`_ to create
graphical interfaces based on Qt Widgets for your Qt for Python project.
-**Qt Designer** is a graphical UI design tool which is available as a
+*Qt Widgets Designer* is a graphical UI design tool which is available as a
standalone binary (``pyside6-designer``) or embedded into the
-`Qt Creator IDE <https://doc.qt.io/qtcreator>`_. Its use within **Qt Creator**
+`Qt Creator IDE <https://doc.qt.io/qtcreator>`_. Its use within *Qt Creator*
is described at
-`Using Qt Designer <https://doc.qt.io/qtcreator/creator-using-qt-designer.html>`_.
+`Using Qt Widgets Designer <https://doc.qt.io/qtcreator/creator-using-qt-designer.html>`_.
.. image:: uifiles.png
:alt: Designer and the equivalent code
be converted to Python or C++ code populating a widget instance at project build
time by the `pyside6-uic <https://doc.qt.io/qt-6/uic.html>`_ tool.
-To create a new Qt Design Form in **Qt Creator**, choose
+To create a new Qt Design Form in *Qt Creator*, choose
``File/New File Or Project`` and "Main Window" for template. Save it as
``mainwindow.ui``. Add a ``QPushButton`` to the center of the centralwidget.
.. _designer_custom_widgets:
-Custom Widgets in Qt Designer
-=============================
+Custom Widgets in Qt Widgets Designer
+=====================================
-**Qt Designer** is able to use user-provided (custom) widgets. They are shown
-in the widget box and can be dragged onto the form just like Qt's widgets (see
-`Using Custom Widgets with Qt Designer <https://doc.qt.io/qt-6/designer-using-custom-widgets.html>`_
-). Normally, this requires implementing the widget as a plugin to Qt Designer
-written in C++ implementing its
+*Qt Widgets Designer* is able to use user-provided (custom) widgets.
+They are shown in the widget box and can be dragged onto the form just like
+Qt's widgets (see
+`Using Custom Widgets with Qt Widgets Designer <https://doc.qt.io/qt-6/designer-using-custom-widgets.html>`_
+). Normally, this requires implementing the widget as a plugin to
+*Qt Widgets Designer* written in C++ implementing its
`QDesignerCustomWidgetInterface`_ .
Qt for Python provides a simple interface for this which is similar to
:meth:`registerCustomWidget()<PySide6.QtUiTools.QUiLoader.registerCustomWidget>`.
The widget needs to be provided as a Python module, as shown by
-the widgetbinding example (file ``wigglywidget.py``) or
-the taskmenuextension example (file ``tictactoe.py``).
+the :ref:`widgetbinding-example` (file ``wigglywidget.py``) or
+the :ref:`task-menu-extension-example` (file ``tictactoe.py``).
-Registering this with Qt Designer is done by providing
+Registering this with *Qt Widgets Designer* is done by providing
a registration script named ``register*.py`` and pointing
the path-type environment variable ``PYSIDE_DESIGNER_PLUGINS``
to the directory.
QPyDesignerCustomWidgetCollection provides an implementation of
`QDesignerCustomWidgetCollectionInterface`_
-exposing custom widgets to **Qt Designer** with static convenience functions
-for registering types or adding instances of
+exposing custom widgets to *Qt Widgets Designer* with static convenience
+functions for registering types or adding instances of
`QDesignerCustomWidgetInterface`_ .
The function
:meth:`registerCustomWidget()<PySide6.QtDesigner.QPyDesignerCustomWidgetCollection.registerCustomWidget>`
-is used to register a widget type with **Qt Designer**. In the simple case, it
+is used to register a widget type with *Qt Widgets Designer*. In the simple case, it
can be used like ``QUiLoader.registerCustomWidget()``. It takes the custom widget
type and some optional keyword arguments passing values that correspond to the
getters of
`QDesignerCustomWidgetInterface`_ :
-When launching **Qt Designer** via its launcher ``pyside6-designer``,
+When launching *Qt Widgets Designer* via its launcher ``pyside6-designer``,
the custom widget should be visible in the widget box.
For advanced usage, it is also possible to pass the function an implementation
.. _QDesignerCustomWidgetCollectionInterface: https://doc.qt.io/qt-6/qdesignercustomwidgetcollectioninterface.html
.. _QDesignerCustomWidgetInterface: https://doc.qt.io/qt-6/qdesignercustomwidgetinterface.html
-Troubleshooting the Qt Designer Plugin
-++++++++++++++++++++++++++++++++++++++
+Troubleshooting the Qt Widgets Designer Plugin
+++++++++++++++++++++++++++++++++++++++++++++++
- The launcher ``pyside6-designer`` must be used. The standalone
- **Qt Designer** will not load the plugin.
+ *Qt Widgets Designer* will not load the plugin.
- The menu item **Help/About Plugin** brings up a dialog showing the plugins
found and potential load error messages.
- Check the console or Windows Debug view for further error messages.
- Due to the buffering of output by Python, error messages may appear
- only after **Qt Designer** has terminated.
+ only after *Qt Widgets Designer* has terminated.
- When building Qt for Python, be sure to set the ``--standalone`` option
for the plugin to be properly installed.
corresponding C++ extension of the PySide module. This is done by attaching your
debugger to the Python interpreter. In this tutorial, we are going to take you
through a comprehensive guide in building Qt 6, using the built Qt 6 to build
-PySide6, and then starting a debugging process in either Qt Creator or VSCode.
+PySide6, and then starting a debugging process in either *Qt Creator* or VSCode.
With VSCode, you should be able to see the combined call stacks for both C++ and
-Python together. With Qt Creator, unfortunately you would only be able to
+Python together. With *Qt Creator*, unfortunately you would only be able to
debug the native C++ code of the PySide module; that is you won't be able to set
breakpoints inside the Python code.
Debugging the process using your preferred IDE
----------------------------------------------
-The following sections guide you through the setup for Qt Creator or VSCode.
+The following sections guide you through the setup for *Qt Creator* or VSCode.
.. toctree::
:glob:
Using Qt Creator's QML Debugger for a PySide6 QML Application
*************************************************************
-Besides the C++ debugger, Qt Creator provides a `QML debugger`_ which lets you
+Besides the C++ debugger, *Qt Creator* provides a `QML debugger`_ which lets you
inspect JavaScript code. It works by connecting to a socket server run by the
``QmlEngine`` instance. The port is passed on the command line. To enable it,
add the below code to your QML application:
Debugging PySide with Qt Creator (Linux)
****************************************
-As opposed to VSCode, presently Qt Creator does not support mixed mode debugging.
+As opposed to VSCode, presently *Qt Creator* does not support mixed mode debugging.
However, we can debug the C++ implementation of the corresponding Python PySide
-code. Unlike VSCode, Qt Creator provides a very easy interface to attach GDB to
+code. Unlike VSCode, *Qt Creator* provides a very easy interface to attach GDB to
the Python interpreter. It saves you from doing all the extra configuration
steps, that have to be done with VSCode.
import QtQuick.Layouts
import FileSystemModule
+pragma ComponentBehavior: Bound
+
ApplicationWindow {
id: root
+
+ property bool expandPath: false
+ property bool showLineNumbers: true
+ property string currentFilePath: ""
+
width: 1100
height: 600
+ minimumWidth: 200
+ minimumHeight: 100
visible: true
+ color: Colors.background
flags: Qt.Window | Qt.FramelessWindowHint
- title: qsTr("Qt Quick Controls - File System Explorer")
+ title: qsTr("File System Explorer Example")
- property string currentFilePath: ""
- property bool expandPath: false
+ function getInfoText() : string {
+ let out = root.currentFilePath
+ if (!out)
+ return qsTr("File System Explorer")
+ return root.expandPath ? out : out.substring(out.lastIndexOf("/") + 1, out.length)
+ }
menuBar: MyMenuBar {
- rootWindow: root
-
- infoText: currentFilePath
- ? (expandPath ? currentFilePath
- : currentFilePath.substring(currentFilePath.lastIndexOf("/") + 1, currentFilePath.length))
- : "File System Explorer"
-
+ dragWindow: root
+ infoText: root.getInfoText()
MyMenu {
title: qsTr("File")
Action {
text: qsTr("Increase Font")
- shortcut: "Ctrl++"
- onTriggered: textArea.font.pixelSize += 1
+ shortcut: StandardKey.ZoomIn
+ onTriggered: editor.text.font.pixelSize += 1
}
Action {
text: qsTr("Decrease Font")
- shortcut: "Ctrl+-"
- onTriggered: textArea.font.pixelSize -= 1
+ shortcut: StandardKey.ZoomOut
+ onTriggered: editor.text.font.pixelSize -= 1
}
Action {
- text: expandPath ? qsTr("Toggle Short Path") : qsTr("Toggle Expand Path")
- enabled: currentFilePath
- onTriggered: expandPath = !expandPath
+ text: root.showLineNumbers ? qsTr("Toggle Line Numbers OFF")
+ : qsTr("Toggle Line Numbers ON")
+ shortcut: "Ctrl+L"
+ onTriggered: root.showLineNumbers = !root.showLineNumbers
+ }
+ Action {
+ text: root.expandPath ? qsTr("Toggle Short Path")
+ : qsTr("Toggle Expand Path")
+ enabled: root.currentFilePath
+ onTriggered: root.expandPath = !root.expandPath
+ }
+ Action {
+ text: qsTr("Reset Filesystem")
+ enabled: sidebar.currentTabIndex === 1
+ onTriggered: fileSystemView.rootIndex = undefined
}
Action {
text: qsTr("Exit")
onTriggered: Qt.exit(0)
+ shortcut: StandardKey.Quit
}
}
Action {
text: qsTr("Cut")
shortcut: StandardKey.Cut
- enabled: textArea.selectedText.length > 0
- onTriggered: textArea.cut()
+ enabled: editor.text.selectedText.length > 0
+ onTriggered: editor.text.cut()
}
Action {
text: qsTr("Copy")
shortcut: StandardKey.Copy
- enabled: textArea.selectedText.length > 0
- onTriggered: textArea.copy()
+ enabled: editor.text.selectedText.length > 0
+ onTriggered: editor.text.copy()
}
Action {
text: qsTr("Paste")
shortcut: StandardKey.Paste
- enabled: textArea.canPaste
- onTriggered: textArea.paste()
+ enabled: editor.text.canPaste
+ onTriggered: editor.text.paste()
}
Action {
text: qsTr("Select All")
shortcut: StandardKey.SelectAll
- enabled: textArea.length > 0
- onTriggered: textArea.selectAll()
+ enabled: editor.text.length > 0
+ onTriggered: editor.text.selectAll()
}
Action {
text: qsTr("Undo")
shortcut: StandardKey.Undo
- enabled: textArea.canUndo
- onTriggered: textArea.undo()
+ enabled: editor.text.canUndo
+ onTriggered: editor.text.undo()
}
}
}
-
- Rectangle {
+ // Set up the layout of the main components in a row:
+ // [ Sidebar, Navigation, Editor ]
+ RowLayout {
anchors.fill: parent
- color: Colors.background
-
- RowLayout {
- anchors.fill: parent
- spacing: 0
-
- // Stores the buttons that navigate the application.
- Sidebar {
- id: sidebar
- z: 2
- rootWindow: root
-
- Layout.preferredWidth: 60
- Layout.fillHeight: true
- }
+ spacing: 0
+
+ // Stores the buttons that navigate the application.
+ Sidebar {
+ id: sidebar
+ dragWindow: root
+ Layout.preferredWidth: 50
+ Layout.fillHeight: true
+ }
- // Allows resizing parts of the UI.
- SplitView {
- Layout.fillWidth: true
- Layout.fillHeight: true
-
- handle: Rectangle {
- implicitWidth: 10
- color: SplitHandle.pressed ? Colors.color2 : Colors.background
- border.color: Colors.color2
- opacity: SplitHandle.hovered || SplitHandle.pressed ? 1.0 : 0.0
-
- Behavior on opacity {
- OpacityAnimator {
- duration: 900
- }
+ // Allows resizing parts of the UI.
+ SplitView {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ // Customized handle to drag between the Navigation and the Editor.
+ handle: Rectangle {
+ implicitWidth: 10
+ color: SplitHandle.pressed ? Colors.color2 : Colors.background
+ border.color: SplitHandle.hovered ? Colors.color2 : Colors.background
+ opacity: SplitHandle.hovered || navigationView.width < 15 ? 1.0 : 0.0
+
+ Behavior on opacity {
+ OpacityAnimator {
+ duration: 1400
}
}
+ }
- // We use an inline component to make a reusable TextArea component.
- // This is convenient when the component is only used in one file.
- component MyTextArea: TextArea {
- antialiasing: true
- color: Colors.textFile
- selectedTextColor: Colors.textFile
- selectionColor: Colors.selection
- renderType: Text.QtRendering
- textFormat: TextEdit.PlainText
-
- background: null
- }
-
- Rectangle {
- z: 1
- color: Colors.surface1
-
- SplitView.preferredWidth: 250
- SplitView.fillHeight: true
-
- StackLayout {
- currentIndex: sidebar.currentTabIndex > 1 ? 1 : sidebar.currentTabIndex
-
- anchors.fill: parent
-
- // Shows the help text.
- MyTextArea {
- readOnly: true
- text: qsTr("This example shows how to use and visualize the file system.\n\n"
- + "Customized Qt Quick Components have been used to achieve this look.\n\n"
- + "You can edit the files but they won't be changed on the file system.\n\n"
- + "Click on the folder icon to the left to get started.")
- wrapMode: TextArea.Wrap
- }
-
- // Shows the files on the file system.
- FileSystemView {
- id: fileSystemView
- color: Colors.surface1
+ Rectangle {
+ id: navigationView
+ color: Colors.surface1
+ SplitView.preferredWidth: 250
+ SplitView.fillHeight: true
+ // The stack-layout provides different views, based on the
+ // selected buttons inside the sidebar.
+ StackLayout {
+ anchors.fill: parent
+ currentIndex: sidebar.currentTabIndex > 1 ? 1 : sidebar.currentTabIndex
+
+ // Shows the help text.
+ Text {
+ text: qsTr("This example shows how to use and visualize the file system.\n\n"
+ + "Customized Qt Quick Components have been used to achieve this look.\n\n"
+ + "You can edit the files but they won't be changed on the file system.\n\n"
+ + "Click on the folder icon to the left to get started.")
+ wrapMode: TextArea.Wrap
+ color: Colors.text
+ }
- onFileClicked: (path) => root.currentFilePath = path
- }
+ // Shows the files on the file system.
+ FileSystemView {
+ id: fileSystemView
+ color: Colors.surface1
+ onFileClicked: path => root.currentFilePath = path
}
}
+ }
- // The ScrollView that contains the TextArea which shows the file's content.
- StackLayout {
- currentIndex: sidebar.currentTabIndex > 1 ? 1 : 0
-
- SplitView.fillWidth: true
- SplitView.fillHeight: true
- // TextArea is the first element inside the stack
- ScrollView {
- Layout.fillWidth: true
- Layout.fillHeight: true
-
- leftPadding: 20
- topPadding: 20
- bottomPadding: 20
+ // The main view that contains the editor or the scheme-manager.
+ StackLayout {
+ currentIndex: sidebar.currentTabIndex > 1 ? 1 : 0
- clip: true
+ SplitView.fillWidth: true
+ SplitView.fillHeight: true
- property alias textArea: textArea
+ Editor {
+ id: editor
+ showLineNumbers: root.showLineNumbers
+ currentFilePath: root.currentFilePath
+ }
- MyTextArea {
- id: textArea
- text: FileSystemModel.readFile(root.currentFilePath)
- }
- }
- // The ColorScheme is the second element in the stack
- ColorScheme {
- Layout.fillWidth: true
- Layout.fillHeight: true
- }
+ ColorScheme {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
}
+
}
}
- ResizeButton {}
+ }
+
+ ResizeButton {
+ resizeWindow: root
}
}
<file>qmldir</file>
<file>Main.qml</file>
<file>qml/About.qml</file>
- <file>qml/ColorScheme.qml</file>
+ <file>qml/Editor.qml</file>
+ <file>qml/Colors.qml</file>
<file>qml/FileSystemView.qml</file>
<file>qml/Icon.qml</file>
<file>qml/MyMenu.qml</file>
<RCC>
<qresource>
+ <file>icons/app_icon.svg</file>
<file>icons/folder_closed.svg</file>
<file>icons/folder_open.svg</file>
<file>icons/generic_file.svg</file>
<file>icons/globe.svg</file>
<file>icons/info_sign.svg</file>
+ <file>icons/leaf.svg</file>
<file>icons/light_bulb.svg</file>
<file>icons/qt_logo.svg</file>
<file>icons/read.svg</file>
<file>icons/resize.svg</file>
- <file>icons/leaf.svg</file>
</qresource>
</RCC>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill="#EBDBB2" d="M13.25 8.5a.75.75 0 1 1-.75-.75.75.75 0 0 1 .75.75zM9.911 21.35l.816.578C10.819 21.798 13 18.666 13 13h-1a15.503 15.503 0 0 1-2.089 8.35zM4 6.703V10a2.002 2.002 0 0 1-2 2v1a2.002 2.002 0 0 1 2 2v3.297A3.707 3.707 0 0 0 7.703 22H9v-1H7.703A2.706 2.706 0 0 1 5 18.297V15a2.999 2.999 0 0 0-1.344-2.5A2.999 2.999 0 0 0 5 10V6.703A2.706 2.706 0 0 1 7.703 4H9V3H7.703A3.707 3.707 0 0 0 4 6.703zM20 10V6.703A3.707 3.707 0 0 0 16.297 3H15v1h1.297A2.706 2.706 0 0 1 19 6.703V10a2.999 2.999 0 0 0 1.344 2.5A2.999 2.999 0 0 0 19 15v3.297A2.706 2.706 0 0 1 16.297 21H15v1h1.297A3.707 3.707 0 0 0 20 18.297V15a2.002 2.002 0 0 1 2-2v-1a2.002 2.002 0 0 1-2-2z"/><path fill="none" d="M0 0h24v24H0z"/></svg>
ApplicationWindow {
id: root
- width: 500
- height: 360
+ width: 650
+ height: 550
flags: Qt.Window | Qt.FramelessWindowHint
color: Colors.surface1
menuBar: MyMenuBar {
id: menuBar
- implicitHeight: 20
- rootWindow: root
+
+ dragWindow: root
+ implicitHeight: 27
infoText: "About Qt"
}
Image {
id: logo
+
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: 20
+
source: "../icons/qt_logo.svg"
- sourceSize: Qt.size(80, 80)
+ sourceSize.width: 80
+ sourceSize.height: 80
fillMode: Image.PreserveAspectFit
+
smooth: true
antialiasing: true
asynchronous: true
}
- TextArea {
- anchors.top: logo.bottom
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.bottom: parent.bottom
- anchors.margins: 20
- antialiasing: true
- wrapMode: Text.WrapAnywhere
- color: Colors.textFile
- horizontalAlignment: Text.AlignHCenter
- readOnly: true
- selectionColor: Colors.selection
- text: qsTr("Qt Group (Nasdaq Helsinki: QTCOM) is a global software company with a strong \
-presence in more than 70 industries and is the leading independent technology behind 1+ billion \
-devices and applications. Qt is used by major global companies and developers worldwide, and the \
-technology enables its customers to deliver exceptional user experiences and advance their digital \
-transformation initiatives. Qt achieves this through its cross-platform software framework for the \
-development of apps and devices, under both commercial and open-source licenses.")
- background: Rectangle {
- color: "transparent"
- }
+ ScrollView {
+ anchors.top: logo.bottom
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ anchors.margins: 20
+
+ TextArea {
+ selectedTextColor: Colors.textFile
+ selectionColor: Colors.selection
+ horizontalAlignment: Text.AlignHCenter
+ textFormat: Text.RichText
+
+ text: qsTr("<h3>About Qt</h3>"
+ + "<p>This program uses Qt version %1.</p>"
+ + "<p>Qt is a C++ toolkit for cross-platform application "
+ + "development.</p>"
+ + "<p>Qt provides single-source portability across all major desktop "
+ + "operating systems. It is also available for embedded Linux and other "
+ + "embedded and mobile operating systems.</p>"
+ + "<p>Qt is available under multiple licensing options designed "
+ + "to accommodate the needs of our various users.</p>"
+ + "<p>Qt licensed under our commercial license agreement is appropriate "
+ + "for development of proprietary/commercial software where you do not "
+ + "want to share any source code with third parties or otherwise cannot "
+ + "comply with the terms of GNU (L)GPL.</p>"
+ + "<p>Qt licensed under GNU (L)GPL is appropriate for the "
+ + "development of Qt applications provided you can comply with the terms "
+ + "and conditions of the respective licenses.</p>"
+ + "<p>Please see <a href=\"http://%2/\">%2</a> "
+ + "for an overview of Qt licensing.</p>"
+ + "<p>Copyright (C) %3 The Qt Company Ltd and other "
+ + "contributors.</p>"
+ + "<p>Qt and the Qt logo are trademarks of The Qt Company Ltd.</p>"
+ + "<p>Qt is The Qt Company Ltd product developed as an open source "
+ + "project. See <a href=\"http://%4/\">%4</a> for more information.</p>")
+ .arg(Application.version).arg("qt.io/licensing").arg("2023").arg("qt.io")
+ color: Colors.textFile
+ wrapMode: Text.WordWrap
+ readOnly: true
+ antialiasing: true
+ background: null
+
+ onLinkActivated: function(link) {
+ Qt.openUrlExternally(link)
+ }
+ }
+ }
+
+ ResizeButton {
+ resizeWindow: root
}
- ResizeButton {}
}
--- /dev/null
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+import FileSystemModule
+
+pragma ComponentBehavior: Bound
+
+// This is the text editor that displays the currently open file, including
+// their corresponding line numbers.
+Rectangle {
+ id: root
+
+ required property string currentFilePath
+ required property bool showLineNumbers
+ property alias text: textArea
+ property int currentLineNumber: -1
+ property int rowHeight: Math.ceil(fontMetrics.lineSpacing)
+
+ color: Colors.background
+
+ onWidthChanged: textArea.update()
+ onHeightChanged: textArea.update()
+
+ RowLayout {
+ anchors.fill: parent
+ // We use a flickable to synchronize the position of the editor and
+ // the line numbers. This is necessary because the line numbers can
+ // extend the available height.
+ Flickable {
+ id: lineNumbers
+
+ // Calculate the width based on the logarithmic scale.
+ Layout.preferredWidth: fontMetrics.averageCharacterWidth
+ * (Math.floor(Math.log10(textArea.lineCount)) + 1) + 10
+ Layout.fillHeight: true
+
+ interactive: false
+ contentY: editorFlickable.contentY
+ visible: textArea.text !== "" && root.showLineNumbers
+
+ Column {
+ anchors.fill: parent
+ Repeater {
+ id: repeatedLineNumbers
+
+ model: LineNumberModel {
+ lineCount: textArea.text !== "" ? textArea.lineCount : 0
+ }
+
+ delegate: Item {
+ required property int index
+
+ width: parent.width
+ height: root.rowHeight
+ Label {
+ id: numbers
+
+ text: parent.index + 1
+
+ width: parent.width
+ height: parent.height
+ horizontalAlignment: Text.AlignLeft
+ verticalAlignment: Text.AlignVCenter
+
+ color: (root.currentLineNumber === parent.index)
+ ? Colors.iconIndicator : Qt.darker(Colors.text, 2)
+ font: textArea.font
+ }
+ Rectangle {
+ id: indicator
+
+ anchors.left: numbers.right
+ width: 1
+ height: parent.height
+ color: Qt.darker(Colors.text, 3)
+ }
+ }
+ }
+ }
+ }
+
+ Flickable {
+ id: editorFlickable
+
+ property alias textArea: textArea
+
+ // We use an inline component to customize the horizontal and vertical
+ // scroll-bars. This is convenient when the component is only used in one file.
+ component MyScrollBar: ScrollBar {
+ id: scrollBar
+ background: Rectangle {
+ implicitWidth: scrollBar.interactive ? 8 : 4
+ implicitHeight: scrollBar.interactive ? 8 : 4
+
+ opacity: scrollBar.active && scrollBar.size < 1.0 ? 1.0 : 0.0
+ color: Colors.background
+ Behavior on opacity {
+ OpacityAnimator {
+ duration: 500
+ }
+ }
+ }
+ contentItem: Rectangle {
+ implicitWidth: scrollBar.interactive ? 8 : 4
+ implicitHeight: scrollBar.interactive ? 8 : 4
+ opacity: scrollBar.active && scrollBar.size < 1.0 ? 1.0 : 0.0
+ color: Colors.color1
+ Behavior on opacity {
+ OpacityAnimator {
+ duration: 1000
+ }
+ }
+ }
+ }
+
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ ScrollBar.horizontal: MyScrollBar {}
+ ScrollBar.vertical: MyScrollBar {}
+
+ boundsBehavior: Flickable.StopAtBounds
+
+ TextArea.flickable: TextArea {
+ id: textArea
+ anchors.fill: parent
+
+ focus: false
+ topPadding: 0
+ leftPadding: 10
+
+ text: FileSystemModel.readFile(root.currentFilePath)
+ tabStopDistance: fontMetrics.averageCharacterWidth * 4
+
+ // Grab the current line number from the C++ interface.
+ onCursorPositionChanged: {
+ root.currentLineNumber = FileSystemModel.currentLineNumber(
+ textArea.textDocument, textArea.cursorPosition)
+ }
+
+ color: Colors.textFile
+ selectedTextColor: Colors.textFile
+ selectionColor: Colors.selection
+
+ textFormat: TextEdit.PlainText
+ renderType: Text.QtRendering
+ selectByMouse: true
+ antialiasing: true
+ background: null
+ }
+
+ FontMetrics {
+ id: fontMetrics
+ font: textArea.font
+ }
+ }
+ }
+}
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
-import QtQuick.Layouts
+import QtQuick.Effects
import QtQuick.Controls.Basic
import FileSystemModule
+pragma ComponentBehavior: Bound
+
// This is the file system view which gets populated by the C++ model.
Rectangle {
id: root
signal fileClicked(string filePath)
+ property alias rootIndex: fileSystemTreeView.rootIndex
TreeView {
id: fileSystemTreeView
+
+ property int lastIndex: -1
+
anchors.fill: parent
model: FileSystemModel
+ rootIndex: FileSystemModel.rootIndex
boundsBehavior: Flickable.StopAtBounds
boundsMovement: Flickable.StopAtBounds
clip: true
- property int lastIndex: -1
-
Component.onCompleted: fileSystemTreeView.toggleExpanded(0)
// The delegate represents a single entry in the filesystem.
implicitWidth: fileSystemTreeView.width > 0 ? fileSystemTreeView.width : 250
implicitHeight: 25
+ // Since we have the 'ComponentBehavior Bound' pragma, we need to
+ // require these properties from our model. This is a convenient way
+ // to bind the properties provided by the model's role names.
required property int index
required property url filePath
+ required property string fileName
- indicator: null
-
- contentItem: Item {
- anchors.fill: parent
+ indicator: Image {
+ id: directoryIcon
- Icon {
- id: directoryIcon
- x: leftMargin + (depth * indentation)
- anchors.verticalCenter: parent.verticalCenter
- path: treeDelegate.hasChildren
- ? (treeDelegate.expanded ? "../icons/folder_open.svg" : "../icons/folder_closed.svg")
+ x: treeDelegate.leftMargin + (treeDelegate.depth * treeDelegate.indentation)
+ anchors.verticalCenter: parent.verticalCenter
+ source: treeDelegate.hasChildren ? (treeDelegate.expanded
+ ? "../icons/folder_open.svg" : "../icons/folder_closed.svg")
: "../icons/generic_file.svg"
- iconColor: (treeDelegate.expanded && treeDelegate.hasChildren) ? Colors.color2 : Colors.folder
- }
- Text {
- anchors.left: directoryIcon.right
- anchors.verticalCenter: parent.verticalCenter
- width: parent.width
- text: model.fileName
- color: Colors.text
- }
+ sourceSize.width: 20
+ sourceSize.height: 20
+ fillMode: Image.PreserveAspectFit
+
+ smooth: true
+ antialiasing: true
+ asynchronous: true
+ }
+
+ contentItem: Text {
+ text: treeDelegate.fileName
+ color: Colors.text
}
background: Rectangle {
- color: treeDelegate.index === fileSystemTreeView.lastIndex
+ color: (treeDelegate.index === fileSystemTreeView.lastIndex)
? Colors.selection
: (hoverHandler.hovered ? Colors.active : "transparent")
}
- TapHandler {
- onSingleTapped: {
- fileSystemTreeView.toggleExpanded(row)
- fileSystemTreeView.lastIndex = index
- // If this model item doesn't have children, it means it's representing a file.
- if (!treeDelegate.hasChildren)
- root.fileClicked(filePath)
+ // We color the directory icons with this MultiEffect, where we overlay
+ // the colorization color ontop of the SVG icons.
+ MultiEffect {
+ id: iconOverlay
+
+ anchors.fill: directoryIcon
+ source: directoryIcon
+ colorization: 1.0
+ brightness: 1.0
+ colorizationColor: {
+ const isFile = treeDelegate.index === fileSystemTreeView.lastIndex
+ && !treeDelegate.hasChildren;
+ if (isFile)
+ return Qt.lighter(Colors.folder, 3)
+
+ const isExpandedFolder = treeDelegate.expanded && treeDelegate.hasChildren;
+ if (isExpandedFolder)
+ return Colors.color2
+ else
+ return Colors.folder
}
}
+
HoverHandler {
id: hoverHandler
}
+
+ TapHandler {
+ acceptedButtons: Qt.LeftButton | Qt.RightButton
+ onSingleTapped: (eventPoint, button) => {
+ switch (button) {
+ case Qt.LeftButton:
+ fileSystemTreeView.toggleExpanded(treeDelegate.row)
+ fileSystemTreeView.lastIndex = treeDelegate.index
+ // If this model item doesn't have children, it means it's
+ // representing a file.
+ if (!treeDelegate.hasChildren)
+ root.fileClicked(treeDelegate.filePath)
+ break;
+ case Qt.RightButton:
+ if (treeDelegate.hasChildren)
+ contextMenu.popup();
+ break;
+ }
+ }
+ }
+
+ MyMenu {
+ id: contextMenu
+ Action {
+ text: qsTr("Set as root index")
+ onTriggered: {
+ fileSystemTreeView.rootIndex = fileSystemTreeView.index(treeDelegate.row, 0)
+ }
+ }
+ Action {
+ text: qsTr("Reset root index")
+ onTriggered: fileSystemTreeView.rootIndex = undefined
+ }
+ }
}
// Provide our own custom ScrollIndicator for the TreeView.
contentItem: Rectangle {
implicitWidth: 6
implicitHeight: 6
+
color: Colors.color1
opacity: fileSystemTreeView.movingVertically ? 0.5 : 0.0
+++ /dev/null
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQuick.Effects
-
-// Custom Component for displaying Icons
-Item {
- id: root
-
- required property url path
- property real padding: 5
- property real size: 30
- property alias iconColor: overlay.colorizationColor
- property alias hovered: mouse.hovered
-
- width: size
- height: size
-
- Image {
- id: icon
- anchors.fill: root
- anchors.margins: padding
- source: path
- sourceSize: Qt.size(size, size)
- fillMode: Image.PreserveAspectFit
- smooth: true
- antialiasing: true
- asynchronous: true
- }
-
- MultiEffect {
- id: overlay
- anchors.fill: icon
- source: icon
- colorization: 1.0
- brightness: 1.0
- }
-
- HoverHandler {
- id: mouse
- acceptedDevices: PointerDevice.Mouse
- }
-}
Menu {
id: root
- background: Rectangle {
- implicitWidth: 200
- implicitHeight: 40
- color: Colors.surface2
- }
-
delegate: MenuItem {
id: menuItem
- implicitWidth: 200
- implicitHeight: 40
contentItem: Item {
Text {
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 5
+
text: menuItem.text
color: enabled ? Colors.text : Colors.disabledText
}
Rectangle {
+ id: indicator
+
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
width: 6
height: parent.height
+
visible: menuItem.highlighted
color: Colors.color2
}
}
background: Rectangle {
+ implicitWidth: 210
+ implicitHeight: 35
color: menuItem.highlighted ? Colors.active : "transparent"
}
}
+ background: Rectangle {
+ implicitWidth: 210
+ implicitHeight: 35
+ color: Colors.surface2
+ }
}
import QtQuick.Controls.Basic
import FileSystemModule
-// The MenuBar also serves as a controller for our Window as we don't use any decorations.
+// The MenuBar also serves as a controller for our window as we don't use any decorations.
MenuBar {
id: root
- required property ApplicationWindow rootWindow
+ required property ApplicationWindow dragWindow
property alias infoText: windowInfo.text
- implicitHeight: 25
-
- // The top level menus on the left side
+ // Customization of the top level menus inside the MenuBar
delegate: MenuBarItem {
id: menuBarItem
- implicitHeight: 25
contentItem: Text {
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
- color: menuBarItem.highlighted ? Colors.textFile : Colors.text
- opacity: enabled ? 1.0 : 0.3
+
text: menuBarItem.text
- elide: Text.ElideRight
font: menuBarItem.font
+ elide: Text.ElideRight
+ color: menuBarItem.highlighted ? Colors.textFile : Colors.text
+ opacity: enabled ? 1.0 : 0.3
}
background: Rectangle {
+ id: background
+
color: menuBarItem.highlighted ? Colors.selection : "transparent"
Rectangle {
id: indicator
+
width: 0; height: 3
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
- color: Colors.color1
+ color: Colors.color1
states: State {
- name: "active"; when: menuBarItem.highlighted
- PropertyChanges { target: indicator; width: parent.width }
+ name: "active"
+ when: menuBarItem.highlighted
+ PropertyChanges {
+ indicator.width: background.width - 2
+ }
}
-
transitions: Transition {
NumberAnimation {
properties: "width"
- duration: 300
+ duration: 175
}
}
-
}
}
}
+ // We use the contentItem property as a place to attach our window decorations. Beneath
+ // the usual menu entries within a MenuBar, it includes a centered information text, along
+ // with the minimize, maximize, and close buttons.
+ contentItem: RowLayout {
+ id: windowBar
- // The background property contains an information text in the middle as well as the
- // Minimize, Maximize and Close Buttons.
- background: Rectangle {
- color: Colors.surface2
- // Make the empty space drag the specified root window.
- WindowDragHandler { dragWindow: rootWindow }
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ spacing: root.spacing
+ Repeater {
+ id: menuBarItems
- Text {
- id: windowInfo
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.verticalCenter: parent.verticalCenter
- color: Colors.text
+ Layout.alignment: Qt.AlignLeft
+ model: root.contentModel
}
- component InteractionButton: Rectangle {
- signal action;
- property alias hovered: hoverHandler.hovered
+ Item {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Text {
+ id: windowInfo
+
+ width: parent.width; height: parent.height
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ leftPadding: windowActions.width
+ color: Colors.text
+ clip: true
+ }
+ }
- width: root.height
- anchors.top: parent.top
- anchors.bottom: parent.bottom
- color: hovered ? Colors.background : "transparent"
+ RowLayout {
+ id: windowActions
- HoverHandler { id: hoverHandler }
- TapHandler { onTapped: action() }
- }
+ Layout.alignment: Qt.AlignRight
+ Layout.fillHeight: true
- InteractionButton {
- id: minimize
+ spacing: 0
- anchors.right: maximize.left
- onAction: rootWindow.showMinimized()
- Rectangle {
- width: parent.height - 10; height: 2
- anchors.centerIn: parent
- color: parent.hovered ? Colors.iconIndicator : Colors.icon
+ component InteractionButton: Rectangle {
+ id: interactionButton
+
+ signal action()
+ property alias hovered: hoverHandler.hovered
+
+ Layout.fillHeight: true
+ Layout.preferredWidth: height
+
+ color: hovered ? Colors.background : "transparent"
+ HoverHandler {
+ id: hoverHandler
+ }
+ TapHandler {
+ id: tapHandler
+ onTapped: interactionButton.action()
+ }
}
- }
- InteractionButton {
- id: maximize
+ InteractionButton {
+ id: minimize
- anchors.right: close.left
- onAction: rootWindow.showMaximized()
- Rectangle {
- anchors.fill: parent
- anchors.margins: 5
- border.width: 2
- color: "transparent"
- border.color: parent.hovered ? Colors.iconIndicator : Colors.icon
+ onAction: root.dragWindow.showMinimized()
+ Rectangle {
+ anchors.centerIn: parent
+ color: parent.hovered ? Colors.iconIndicator : Colors.icon
+ height: 2
+ width: parent.height - 14
+ }
}
- }
- InteractionButton {
- id: close
+ InteractionButton {
+ id: maximize
- color: hovered ? "#ec4143" : "transparent"
- anchors.right: parent.right
- onAction: rootWindow.close()
- Rectangle {
- width: parent.height - 8; height: 2
- anchors.centerIn: parent
- color: parent.hovered ? Colors.iconIndicator : Colors.icon
- rotation: 45
- transformOrigin: Item.Center
- antialiasing: true
+ onAction: root.dragWindow.showMaximized()
+ Rectangle {
+ anchors.fill: parent
+ anchors.margins: 7
+ border.color: parent.hovered ? Colors.iconIndicator : Colors.icon
+ border.width: 2
+ color: "transparent"
+ }
+ }
+
+ InteractionButton {
+ id: close
+
+ color: hovered ? "#ec4143" : "transparent"
+ onAction: root.dragWindow.close()
Rectangle {
- width: parent.height
- height: parent.width
anchors.centerIn: parent
- color: parent.color
+ width: parent.height - 8; height: 2
+
+ rotation: 45
antialiasing: true
+ transformOrigin: Item.Center
+ color: parent.hovered ? Colors.iconIndicator : Colors.icon
+
+ Rectangle {
+ anchors.centerIn: parent
+ width: parent.height
+ height: parent.width
+
+ antialiasing: true
+ color: parent.color
+ }
}
}
}
}
+ background: Rectangle {
+ color: Colors.surface2
+ // Make the empty space drag the specified root window.
+ WindowDragHandler {
+ dragWindow: root.dragWindow
+ }
+ }
}
import FileSystemModule
Button {
+ required property ApplicationWindow resizeWindow
+
icon.width: 20; icon.height: 20
anchors.right: parent.right
anchors.bottom: parent.bottom
bottomPadding: 3
icon.source: "../icons/resize.svg"
- icon.color: down || checked ? Colors.iconIndicator : Colors.icon
+ icon.color: hovered ? Colors.iconIndicator : Colors.icon
+ background: null
checkable: false
display: AbstractButton.IconOnly
- background: null
- onPressed: {
- root.startSystemResize(Qt.BottomEdge | Qt.RightEdge)
- }
+ onPressed: resizeWindow.startSystemResize(Qt.BottomEdge | Qt.RightEdge)
}
Rectangle {
id: root
+
+ property alias currentTabIndex: topBar.currentIndex
+ required property ApplicationWindow dragWindow
+ readonly property int tabBarSpacing: 10
+
color: Colors.surface2
- required property ApplicationWindow rootWindow
- property alias currentTabIndex: tabBar.currentIndex
+ component SidebarEntry: Button {
+ id: sidebarButton
- ColumnLayout {
- anchors.fill: root
- anchors.topMargin: 10
- anchors.bottomMargin: 10
- spacing: 10
+ Layout.alignment: Qt.AlignHCenter
+ Layout.fillWidth: true
- // TabBar is designed to be horizontal, whereas we need a vertical bar.
- // We can easily achieve that by using a Container.
- Container {
- id: tabBar
+ icon.color: down || checked ? Colors.iconIndicator : Colors.icon
+ icon.width: 27
+ icon.height: 27
- Layout.fillWidth: true
+ topPadding: 0
+ rightPadding: 0
+ bottomPadding: 0
+ leftPadding: 0
+ background: null
- // ButtonGroup ensures that only one button can be checked at a time.
- ButtonGroup {
- buttons: tabBar.contentItem.children
- // We have to manage the currentIndex ourselves, which we do by setting it to the
- // index of the currently checked button.
- // We use setCurrentIndex instead of setting the currentIndex property to avoid breaking bindings.
- // See "Managing the Current Index" in Container's documentation for more information.
- onCheckedButtonChanged: tabBar.setCurrentIndex(Math.max(0, buttons.indexOf(checkedButton)))
- }
+ Rectangle {
+ id: indicator
- contentItem: ColumnLayout {
- spacing: tabBar.spacing
+ anchors.verticalCenter: parent.verticalCenter
+ x: 2
+ width: 4
+ height: sidebarButton.icon.height * 1.2
- Repeater {
- model: tabBar.contentModel
- }
- }
+ visible: sidebarButton.checked
+ color: Colors.color1
+ }
+ }
+
+ // TabBar is designed to be horizontal, whereas we need a vertical bar.
+ // We can easily achieve that by using a Container.
+ component TabBar: Container {
+ id: tabBarComponent
+
+ Layout.fillWidth: true
+ // ButtonGroup ensures that only one button can be checked at a time.
+ ButtonGroup {
+ buttons: tabBarComponent.contentChildren
+
+ // We have to manage the currentIndex ourselves, which we do by setting it to the index
+ // of the currently checked button. We use setCurrentIndex instead of setting the
+ // currentIndex property to avoid breaking bindings. See "Managing the Current Index"
+ // in Container's documentation for more information.
+ onCheckedButtonChanged: tabBarComponent.setCurrentIndex(
+ Math.max(0, buttons.indexOf(checkedButton)))
+ }
- component SidebarEntry: Button {
- id: sidebarButton
- icon.color: down || checked ? Colors.iconIndicator : Colors.icon
- icon.width: 35
- icon.height: 35
- leftPadding: 8 + indicator.width
-
- background: null
-
- Rectangle {
- id: indicator
- x: 4
- anchors.verticalCenter: parent.verticalCenter
- width: 4
- height: sidebarButton.icon.width
- color: Colors.color1
- visible: sidebarButton.checked
- }
+ contentItem: ColumnLayout {
+ spacing: tabBarComponent.spacing
+ Repeater {
+ model: tabBarComponent.contentModel
}
+ }
+ }
+ ColumnLayout {
+ anchors.fill: root
+ anchors.topMargin: root.tabBarSpacing
+ anchors.bottomMargin: root.tabBarSpacing
+
+ spacing: root.tabBarSpacing
+ TabBar {
+ id: topBar
+
+ spacing: root.tabBarSpacing
// Shows help text when clicked.
SidebarEntry {
+ id: infoTab
icon.source: "../icons/light_bulb.svg"
checkable: true
checked: true
-
- Layout.alignment: Qt.AlignHCenter
}
// Shows the file system when clicked.
SidebarEntry {
+ id: filesystemTab
+
icon.source: "../icons/read.svg"
checkable: true
-
- Layout.alignment: Qt.AlignHCenter
}
+
// Shows the scheme switcher
SidebarEntry {
icon.source: "../icons/leaf.svg"
Layout.fillWidth: true
// Make the empty space drag our main window.
- WindowDragHandler { dragWindow: rootWindow }
+ WindowDragHandler {
+ dragWindow: root.dragWindow
+ }
}
- // Opens the Qt website in the system's web browser.
- SidebarEntry {
- id: qtWebsiteButton
- icon.source: "../icons/globe.svg"
- checkable: false
+ TabBar {
+ id: bottomBar
- onClicked: Qt.openUrlExternally("https://www.qt.io/")
- }
+ spacing: root.tabBarSpacing
+ // Opens the Qt website in the system's web browser.
+ SidebarEntry {
+ id: qtWebsiteButton
+ icon.source: "../icons/globe.svg"
+ checkable: false
+ onClicked: Qt.openUrlExternally("https://www.qt.io/")
+ }
- // Opens the About Qt Window.
- SidebarEntry {
- id: aboutQtButton
- icon.source: "../icons/info_sign.svg"
- checkable: false
+ // Opens the About Qt Window.
+ SidebarEntry {
+ id: aboutQtButton
- onClicked: aboutQtWindow.visible = !aboutQtWindow.visible
+ icon.source: "../icons/info_sign.svg"
+ checkable: false
+ onClicked: aboutQtWindow.visible = !aboutQtWindow.visible
+ }
}
}
module FileSystemModule
Main 1.0 Main.qml
-Icon 1.0 qml/Icon.qml
About 1.0 qml/About.qml
+Editor 1.0 qml/Editor.qml
MyMenu 1.0 qml/MyMenu.qml
Sidebar 1.0 qml/Sidebar.qml
MyMenuBar 1.0 qml/MyMenuBar.qml
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtWidgets import QFileSystemModel
+from PySide6.QtQuick import QQuickTextDocument
+from PySide6.QtQml import QmlElement, QmlSingleton
+from PySide6.QtCore import (Qt, QDir, QAbstractListModel, Slot, QFile, QTextStream,
+ QMimeDatabase, QFileInfo, QStandardPaths, QModelIndex,
+ Signal, Property)
+
+QML_IMPORT_NAME = "FileSystemModule"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+@QmlSingleton
+class FileSystemModel(QFileSystemModel):
+
+ rootIndexChanged = Signal()
+
+ def getDefaultRootDir():
+ return QStandardPaths.writableLocation(QStandardPaths.StandardLocation.HomeLocation)
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+ self.mRootIndex = QModelIndex()
+ self.mDb = QMimeDatabase()
+ self.setFilter(QDir.Filter.AllEntries | QDir.Filter.Hidden | QDir.Filter.NoDotAndDotDot)
+ self.setInitialDirectory()
+
+ # check for the correct mime type and then read the file.
+ # returns the text file's content or an error message on failure
+ @Slot(str, result=str)
+ def readFile(self, path):
+ if path == "":
+ return ""
+
+ file = QFile(path)
+
+ mime = self.mDb.mimeTypeForFile(QFileInfo(file))
+ if ('text' in mime.comment().lower()
+ or any('text' in s.lower() for s in mime.parentMimeTypes())):
+ if file.open(QFile.OpenModeFlag.ReadOnly | QFile.OpenModeFlag.Text):
+ stream = QTextStream(file).readAll()
+ file.close()
+ return stream
+ else:
+ return self.tr("Error opening the file!")
+ return self.tr("File type not supported!")
+
+ @Slot(QQuickTextDocument, int, result=int)
+ def currentLineNumber(self, textDocument, cursorPosition):
+ td = textDocument.textDocument()
+ tb = td.findBlock(cursorPosition)
+ return tb.blockNumber()
+
+ def setInitialDirectory(self, path=getDefaultRootDir()):
+ dir = QDir(path)
+ if dir.makeAbsolute():
+ self.setRootPath(dir.path())
+ else:
+ self.setRootPath(self.getDefaultRootDir())
+ self.setRootIndex(self.index(dir.path()))
+
+ # we only need one column in this example
+ def columnCount(self, parent):
+ return 1
+
+ @Property(QModelIndex, notify=rootIndexChanged)
+ def rootIndex(self):
+ return self.mRootIndex
+
+ def setRootIndex(self, index):
+ if (index == self.mRootIndex):
+ return
+ self.mRootIndex = index
+ self.rootIndexChanged.emit()
+
+
+@QmlElement
+class LineNumberModel(QAbstractListModel):
+
+ lineCountChanged = Signal()
+
+ def __init__(self, parent=None):
+ self.mLineCount = 0
+ super().__init__(parent=parent)
+
+ @Property(int, notify=lineCountChanged)
+ def lineCount(self):
+ return self.mLineCount
+
+ @lineCount.setter
+ def lineCount(self, n):
+ if n < 0:
+ print("lineCount must be greater then zero")
+ return
+ if self.mLineCount == n:
+ return
+
+ if self.mLineCount < n:
+ self.beginInsertRows(QModelIndex(), self.mLineCount, n - 1)
+ self.mLineCount = n
+ self.endInsertRows()
+ else:
+ self.beginRemoveRows(QModelIndex(), n, self.mLineCount - 1)
+ self.mLineCount = n
+ self.endRemoveRows()
+
+ def rowCount(self, parent):
+ return self.mLineCount
+
+ def data(self, index, role):
+ if not self.checkIndex(index) or role != Qt.ItemDataRole.DisplayRole:
+ return
+ return index.row()
language: QML
caption: true
linenos: true
-lines: 83-90
+lines: 99-105
---
```
language: QML
caption: true
linenos: true
-lines: 169-198
+lines: 170-187
---
```
language: QML
caption: true
linenos: true
-lines: 144-145
+lines: 147-150
---
```
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-"""
-This example shows how to customize Qt Quick Controls by implementing a simple filesystem explorer.
-"""
-
-# Compile both resource files app.qrc and icons.qrc and include them here if you wish
-# to load them from the resource system. Currently, all resources are loaded locally
-# import FileSystemModule.rc_icons
-# import FileSystemModule.rc_app
-from scheme_manager import SchemeManager
-
-from PySide6.QtWidgets import QFileSystemModel
-from PySide6.QtGui import QGuiApplication
-from PySide6.QtQml import (QQmlApplicationEngine, QmlElement, QmlSingleton)
-from PySide6.QtCore import (Slot, QFile, QTextStream, QMimeDatabase, QFileInfo, QStandardPaths)
-
-import sys
-
-
-QML_IMPORT_NAME = "FileSystemModule"
-QML_IMPORT_MAJOR_VERSION = 1
-
-
-@QmlElement
-@QmlSingleton
-class FileSystemModel(QFileSystemModel):
- def __init__(self, parent=None):
- super().__init__(parent=parent)
- self.setRootPath(QStandardPaths.writableLocation(QStandardPaths.HomeLocation))
- self.db = QMimeDatabase()
-
- # we only need one column in this example
- def columnCount(self, parent):
- return 1
-
- # check for the correct mime type and then read the file.
- # returns the text file's content or an error message on failure
- @Slot(str, result=str)
- def readFile(self, path):
- if path == "":
- return ""
-
- file = QFile(path)
-
- mime = self.db.mimeTypeForFile(QFileInfo(file))
- if 'text' in mime.comment().lower() or any('text' in s.lower() for s in mime.parentMimeTypes()):
- if file.open(QFile.ReadOnly | QFile.Text):
- stream = QTextStream(file).readAll()
- return stream
- else:
- return self.tr("Error opening the file!")
- return self.tr("File type not supported!")
-
-
-if __name__ == '__main__':
- app = QGuiApplication(sys.argv)
- app.setOrganizationName("QtProject")
- app.setApplicationName("File System Explorer")
- engine = QQmlApplicationEngine()
- # Include the path of this file to search for the 'qmldir' module
- engine.addImportPath(sys.path[0])
-
- engine.loadFromModule("FileSystemModule", "Main")
-
- if not engine.rootObjects():
- sys.exit(-1)
-
- sys.exit(app.exec())
{
"files": [
- "extendedexplorer.py",
- "color_scheme.py",
+ "main.py",
+ "editormodels.py",
+ "scheme_manager.py",
+ "schemes.json",
"FileSystemModule/qmldir",
"FileSystemModule/app.qrc",
"FileSystemModule/icons.qrc",
- "FileSystemModule/qmldir",
"FileSystemModule/Main.qml",
"FileSystemModule/qml/About.qml",
"FileSystemModule/qml/ColorScheme.qml",
+ "FileSystemModule/qml/Editor.qml",
"FileSystemModule/qml/FileSystemView.qml",
- "FileSystemModule/qml/Icon.qml",
"FileSystemModule/qml/MyMenu.qml",
"FileSystemModule/qml/MyMenuBar.qml",
"FileSystemModule/qml/ResizeButton.qml",
"FileSystemModule/qml/Sidebar.qml",
"FileSystemModule/qml/WindowDragHandler.qml",
+ "FileSystemModule/icons/app_icon.svg",
"FileSystemModule/icons/folder_closed.svg",
"FileSystemModule/icons/folder_open.svg",
"FileSystemModule/icons/generic_file.svg",
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+"""
+This example shows how to customize Qt Quick Controls by implementing a simple filesystem explorer.
+"""
+
+# Compile both resource files app.qrc and icons.qrc and include them here if you wish
+# to load them from the resource system. Currently, all resources are loaded locally
+# import FileSystemModule.rc_icons
+# import FileSystemModule.rc_app
+
+from scheme_manager import SchemeManager
+from editormodels import FileSystemModel
+import PySide6
+from PySide6.QtGui import QGuiApplication, QIcon
+from PySide6.QtQml import QQmlApplicationEngine
+from PySide6.QtCore import QCommandLineParser
+
+import sys
+
+if __name__ == '__main__':
+ app = QGuiApplication(sys.argv)
+ app.setOrganizationName("QtProject")
+ app.setApplicationName("File System Explorer")
+ app.setApplicationVersion(PySide6.__version__)
+ app.setWindowIcon(QIcon("FileSystemModule/icons/app_icon.svg"))
+
+ parser = QCommandLineParser()
+ parser.setApplicationDescription("Qt Filesystemexplorer Example")
+ parser.addHelpOption()
+ parser.addVersionOption()
+ parser.addPositionalArgument("", "Initial directory", "[path]")
+ parser.process(app)
+ args = parser.positionalArguments()
+
+ engine = QQmlApplicationEngine()
+ # Include the path of this file to search for the 'qmldir' module
+ engine.addImportPath(sys.path[0])
+
+ engine.loadFromModule("FileSystemModule", "Main")
+
+ if not engine.rootObjects():
+ sys.exit(-1)
+
+ if (len(args) == 1):
+ fsm = engine.singletonInstance("FileSystemModule", "FileSystemModel")
+ fsm.setInitialDirectory(args[0])
+
+ sys.exit(app.exec())
Before you begin, install the following prerequisites:
* The `PySide6 <https://pypi.org/project/PySide6/>`_ Python packages.
-* Qt Creator v4.9 beta1 or later from
+* *Qt Creator* from
`https://download.qt.io
<https://download.qt.io/snapshots/qtcreator/>`_.
The following step-by-step instructions guide you through application
-development process using Qt Creator:
+development process using *Qt Creator*:
-#. Open Qt Creator and select **File > New File or Project..** menu item
+#. Open *Qt Creator* and select **File > New File or Project..** menu item
to open following dialog:
.. image:: newpyproject.png
set(CMAKE_AUTOMOC ON)
set(libpyside_HEADERS # installed below
- pysideqslotobject_p.h
class_property.h
dynamicqmetaobject.h
feature_select.h
pysideqhash.h
pysideqmetatype.h
pysideqobject.h
+ pysideqslotobject_p.h
pysidesignal.h
pysidesignal_p.h
pysideslot_p.h
)
set(libpyside_SRC
- pysideqslotobject_p.cpp
class_property.cpp
dynamicqmetaobject.cpp
feature_select.cpp
pysideclassdecorator.cpp
pysideclassinfo.cpp
pysideqenum.cpp
+ pysideqslotobject_p.cpp
pysidemetafunction.cpp
pysidesignal.cpp
pysideslot.cpp
#include "pysidestaticstrings.h"
#include "feature_select.h"
+#include <pep384ext.h>
#include <shiboken.h>
#include <sbkstaticstrings.h>
// `class_property.__get__()`: Always pass the class instead of the instance.
static PyObject *PyClassProperty_descr_get(PyObject *self, PyObject * /*ob*/, PyObject *cls)
{
- return PyProperty_Type.tp_descr_get(self, cls, cls);
+ return PepExt_Type_GetDescrGetSlot(&PyProperty_Type)(self, cls, cls);
}
// `class_property.__set__()`: Just like the above `__get__()`.
static int PyClassProperty_descr_set(PyObject *self, PyObject *obj, PyObject *value)
{
PyObject *cls = PyType_Check(obj) ? obj : reinterpret_cast<PyObject *>(Py_TYPE(obj));
- return PyProperty_Type.tp_descr_set(self, cls, value);
+ return PepExt_Type_GetDescrSetSlot(&PyProperty_Type)(self, cls, value);
}
// PYSIDE-2230: Why is this metaclass necessary?
{
auto hold = Py_TYPE(self);
self->ob_type = &PyProperty_Type;
- auto ret = PyProperty_Type.tp_init(self, args, kwargs);
+ auto ret = PepExt_Type_GetInitSlot(&PyProperty_Type)(self, args, kwargs);
self->ob_type = hold;
return ret;
}
&& !PyObject_IsInstance(value, class_prop);
if (call_descr_set) {
// Call `class_property.__set__()` instead of replacing the `class_property`.
- return Py_TYPE(descr)->tp_descr_set(descr, obj, value);
+ return PepExt_Type_GetDescrSetSlot(Py_TYPE(descr))(descr, obj, value);
} // Replace existing attribute.
- return PyType_Type.tp_setattro(obj, name, value);
+ return PepExt_Type_GetSetAttroSlot(&PyType_Type)(obj, name, value);
}
} // extern "C"
const int index = m_baseObject->indexOfProperty(name);
if (index == -1)
addProperty(name, value);
- } else if (Py_TYPE(value)->tp_call != nullptr) {
+ } else if (PepType_GetSlot(Py_TYPE(value), Py_tp_call) != nullptr) {
// PYSIDE-198: PyFunction_Check does not work with Nuitka.
// Register slots.
if (PyObject_HasAttr(value, slotAttrName)) {
PyObject *ChameleonDict = PepRun_GetResult(R"CPP(if True:
class ChameleonDict(dict):
- __slots__ = ("dict_ring", "select_id")
+ __slots__ = ("dict_ring", "select_id", "orig_dict")
result = ChameleonDict
// insert the dict into itself as ring
setNextDict(new_dict, new_dict);
// We have now an exact copy of the dict with a new type.
- // Replace `__dict__` which usually has refcount 1 (but see cyclic_test.py)
- Py_DECREF(PepType_GetDict(type));
PepType_SetDict(type, new_dict);
+ // PYSIDE-2404: Retain the original dict for easy late init.
+ PyObject_SetAttr(new_dict, PySideName::orig_dict(), dict);
return true;
}
* A 'false' return is fatal.
*/
AutoDecRef dict(PepType_GetDict(type));
+ AutoDecRef orig_dict(PyObject_GetAttr(dict, PySideName::orig_dict()));
auto *ob_ndt = reinterpret_cast<PyObject *>(new_dict_type);
auto *new_dict = PyObject_CallObject(ob_ndt, nullptr);
if (new_dict == nullptr)
setNextDict(dict, new_dict);
setNextDict(new_dict, next_dict);
PepType_SetDict(type, new_dict);
+ // PYSIDE-2404: Retain the original dict for easy late init.
+ PyObject_SetAttr(new_dict, PySideName::orig_dict(), orig_dict);
return true;
}
static bool patch_property_impl();
static bool is_initialized = false;
+static void featureEnableCallback(bool enable)
+{
+ featurePointer = enable ? featureProcArray : nullptr;
+}
+
void init()
{
// This function can be called multiple times.
if (!is_initialized) {
featurePointer = featureProcArray;
initSelectableFeature(SelectFeatureSet);
+ setSelectableFeatureCallback(featureEnableCallback);
patch_property_impl();
is_initialized = true;
}
#include <autodecref.h>
#include <gilstate.h>
+#include <pep384ext.h>
#include <QtCore/QMetaMethod>
#include <QtCore/QSet>
//create a callback based on method data
if (m_isMethod)
- callback = Py_TYPE(m_callback)->tp_descr_get(m_callback, m_pythonSelf, nullptr);
+ callback = PepExt_Type_CallDescrGet(m_callback, m_pythonSelf, nullptr);
else
Py_INCREF(callback);
auto self = reinterpret_cast<DynamicSlotDataV2 *>(data);
self->m_weakRef = nullptr;
Py_BEGIN_ALLOW_THREADS
- SignalManager::instance().deleteGobalReceiver(self->m_parent);
+ SignalManager::instance().deleteGlobalReceiver(self->m_parent);
Py_END_ALLOW_THREADS
}
#include "dynamicqmetaobject.h"
+#include <QtCore/QtCompare>
#include <QtCore/QByteArray>
#include <QtCore/QHashFunctions>
#include <QtCore/QObject>
{
return qHashMulti(seed, k.object, k.method);
}
+ friend constexpr bool comparesEqual(const GlobalReceiverKey &lhs,
+ const GlobalReceiverKey &rhs) noexcept
+ {
+ return lhs.object == rhs.object && lhs.method == rhs.method;
+ }
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(GlobalReceiverKey)
};
-inline bool operator==(const GlobalReceiverKey &k1, const GlobalReceiverKey &k2)
-{
- return k1.object == k2.object && k1.method == k2.method;
-}
-
-inline bool operator!=(const GlobalReceiverKey &k1, const GlobalReceiverKey &k2)
-{
- return k1.object != k2.object || k1.method != k2.method;
-}
-
/// A class used to link C++ Signals to non C++ slots (Python callbacks) by
/// providing fake slots for QObject::connect().
/// It keeps a Python callback and the list of QObject senders. It is stored
#include <sbkstring.h>
#include <sbkstaticstrings.h>
#include <sbkfeature_base.h>
+#include <sbkmodule.h>
#include <QtCore/QByteArray>
#include <QtCore/QCoreApplication>
#include <QtCore/QMutex>
#include <QtCore/QStack>
#include <QtCore/QThread>
+#include <QtCore/private/qobject_p.h>
#include <algorithm>
#include <cstring>
Q_LOGGING_CATEGORY(lcPySide, "qt.pyside.libpyside", QtCriticalMsg)
+static QObjectData *qt_object_private(const QObject *o)
+{
+ class FriendlyQObject : public QObject {
+ public:
+ using QObject::d_ptr;
+ };
+ return static_cast<const FriendlyQObject *>(o)->d_ptr.data();
+}
+
+static bool hasDynamicMetaObject(const QObject *o)
+{
+ return qt_object_private(o)->metaObject != nullptr;
+}
+
namespace PySide
{
TypeUserData *retrieveTypeUserData(PyTypeObject *pyTypeObj)
{
+ if (!SbkObjectType_Check(pyTypeObj))
+ return nullptr;
return reinterpret_cast<TypeUserData *>(Shiboken::ObjectType::getTypeUserData(pyTypeObj));
}
static const char invalidatePropertyName[] = "_PySideInvalidatePtr";
+// PYSIDE-2749: Skip over internal QML classes and classes
+// with dynamic meta objects when looking for the best matching
+// type to avoid unnessarily triggering the lazy load mechanism
+// for classes that do not have a binding from things like eventFilter().
+static inline bool isInternalObject(const char *name)
+{
+ return std::strstr(name, "QMLTYPE") != nullptr || std::strstr(name, "QQmlPrivate") != nullptr;
+}
+
+static const QMetaObject *metaObjectCandidate(const QObject *o)
+{
+ auto *metaObject = o->metaObject();
+ // Skip QML helper types and Python objects
+ if (hasDynamicMetaObject(o)) {
+ if (auto *super = metaObject->superClass())
+ metaObject = super;
+ }
+ for (auto *candidate = metaObject; candidate != nullptr; candidate = candidate->superClass()) {
+ if (!isInternalObject(candidate->className())) {
+ metaObject = candidate;
+ break;
+ }
+ }
+ return metaObject;
+}
+
// PYSIDE-1214, when creating new wrappers for classes inheriting QObject but
// not exposed to Python, try to find the best-matching (most-derived) Qt
// class by walking up the meta objects.
{
const char *typeName = typeid(*cppSelf).name();
if (!Shiboken::Conversions::getConverter(typeName)) {
- for (auto metaObject = cppSelf->metaObject(); metaObject; metaObject = metaObject->superClass()) {
+ auto *metaObject = metaObjectCandidate(cppSelf);
+ for (; metaObject != nullptr; metaObject = metaObject->superClass()) {
const char *name = metaObject->className();
if (Shiboken::Conversions::getConverter(name)) {
typeName = name;
QString executablePath = QString::fromWCharArray(Py_GetProgramFullPath());
#else
// PYSIDE-535: FIXME: Add this function when available.
- QString executablePath = QLatin1String("missing Py_GetProgramFullPath");
+ QString executablePath = QLatin1StringView("missing Py_GetProgramFullPath");
#endif // PYPY_VERSION
QString appDirPath = QFileInfo(executablePath).absolutePath();
{
if (PyType_Check(obj) != 0)
debug << "type: \"" << pyTypeName(obj) << '"';
- else if (PyLong_Check(obj) != 0)
- debug << PyLong_AsLongLong(obj);
- else if (PyFloat_Check(obj) != 0)
+ else if (PyLong_Check(obj) != 0) {
+ const auto llv = PyLong_AsLongLong(obj);
+ if (PyErr_Occurred() != PyExc_OverflowError) {
+ debug << llv;
+ } else {
+ PyErr_Clear();
+ debug << "0x" << Qt::hex << PyLong_AsUnsignedLongLong(obj) << Qt::dec;
+ }
+ } else if (PyFloat_Check(obj) != 0)
debug << PyFloat_AsDouble(obj);
else if (PyUnicode_Check(obj) != 0)
debug << '"' << pyStringToQString(obj) << '"';
return debug;
}
-} //namespace PySide
+debugPyBuffer::debugPyBuffer(Py_buffer *b) noexcept : m_buffer(b)
+{
+}
+
+static void formatPy_ssizeArray(QDebug &debug, const char *name, const Py_ssize_t *array, int len)
+{
+ debug << ", " << name << '=';
+ if (array != nullptr) {
+ debug << '[';
+ for (int i = 0; i < len; ++i)
+ debug << array[i] << ' ';
+ debug << ']';
+ } else {
+ debug << '0';
+ }
+}
+
+PYSIDE_API QDebug operator<<(QDebug debug, const debugPyBuffer &b)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "Py_buffer(";
+ if (b.m_buffer != nullptr) {
+ debug << "obj=" << b.m_buffer->obj
+ << ", buf=" << b.m_buffer->buf << ", len=" << b.m_buffer->len
+ << ", readonly=" << b.m_buffer->readonly
+ << ", itemsize=" << b.m_buffer->itemsize << ", format=";
+ if (b.m_buffer->format != nullptr)
+ debug << '"' << b.m_buffer->format << '"';
+ else
+ debug << '0';
+ debug << ", ndim=" << b.m_buffer->ndim;
+ formatPy_ssizeArray(debug, "shape", b.m_buffer->shape, b.m_buffer->ndim);
+ formatPy_ssizeArray(debug, "strides", b.m_buffer->strides, b.m_buffer->ndim);
+ formatPy_ssizeArray(debug, "suboffsets", b.m_buffer->suboffsets, b.m_buffer->ndim);
+ } else {
+ debug << '0';
+ }
+ debug << ')';
+ return debug;
+}
+} // namespace PySide
#include "pysideqobject.h"
#include <basewrapper.h>
-#include <sbkcppstring.h>
+#include <sbkstring.h>
namespace PySide::ClassDecorator {
if (PyUnicode_Check(arg)) {
auto *pData = DecoratorPrivate::get<StringDecoratorPrivate>(self);
result = 0;
- Shiboken::String::toCppString(arg, &(pData->m_string));
+ pData->m_string.assign(Shiboken::String::toCString(arg));
}
}
return result;
#include <pysidemacros.h>
#include <sbkpython.h>
+#include <pep384ext.h>
-#include <QtCore/qtclasshelpermacros.h>
+#include <QtCore/QByteArray>
#include <array>
#include <string>
/// Init function that retrieves the string parameter using convertToString()
int tp_init(PyObject *self, PyObject *args, PyObject *kwds) override;
- const std::string &string() const { return m_string; }
+ QByteArray string() const { return m_string; }
protected:
/// Helper function that retrieves the string parameter
int convertToString(PyObject *self, PyObject *args);
private:
- std::string m_string;
+ QByteArray m_string;
};
/// Base class for private objects of class decorator with a type parameter
{
static PyObject *tp_new(PyTypeObject *subtype)
{
- auto *result = reinterpret_cast<PySideClassDecorator *>(subtype->tp_alloc(subtype, 0));
+ auto *result = PepExt_TypeCallAlloc<PySideClassDecorator>(subtype, 0);
result->d = new DecoratorPrivate;
return reinterpret_cast<PyObject *>(result);
}
auto pySelf = reinterpret_cast<PyObject *>(self);
auto decorator = reinterpret_cast<PySideClassDecorator *>(self);
delete decorator->d;
- Py_TYPE(pySelf)->tp_base->tp_free(self);
+ PepExt_TypeCallFree(Py_TYPE(pySelf)->tp_base, self);
}
static PyObject *tp_call(PyObject *self, PyObject *args, PyObject *kwds)
auto *pData = DecoratorPrivate::get<ClassInfoPrivate>(self);
- if (pData->m_alreadyWrapped) {
- PyErr_SetString(PyExc_TypeError, "This instance of ClassInfo() was already used to wrap an object");
- return nullptr;
- }
-
- bool validClass = false;
+ if (pData->m_alreadyWrapped)
+ return PyErr_Format(PyExc_TypeError, "This instance of ClassInfo() was already used to wrap an object");
PyTypeObject *klassType = reinterpret_cast<PyTypeObject *>(klass);
- if (auto userData = PySide::retrieveTypeUserData(klassType)) {
- PySide::MetaObjectBuilder &mo = userData->mo;
- mo.addInfo(pData->m_data);
- pData->m_alreadyWrapped = true;
- validClass = true;
- }
+ if (!PySide::ClassInfo::setClassInfo(klassType, pData->m_data))
+ return PyErr_Format(PyExc_TypeError, "This decorator can only be used on classes that are subclasses of QObject");
- if (!validClass) {
- PyErr_SetString(PyExc_TypeError, "This decorator can only be used on classes that are subclasses of QObject");
- return nullptr;
- }
+ pData->m_alreadyWrapped = true;
Py_INCREF(klass);
return klass;
{
PyObject *infoDict = nullptr;
auto size = PyTuple_Size(args);
- if (size == 1 && !kwds) {
+ if (size == 1 && kwds == nullptr) {
PyObject *tmp = PyTuple_GET_ITEM(args, 0);
if (PyDict_Check(tmp))
infoDict = tmp;
infoDict = kwds;
}
- if (!infoDict) {
+ if (infoDict == nullptr) {
PyErr_Format(PyExc_TypeError, "ClassInfo() takes either keyword argument(s) or "
"a single dictionary argument");
return -1;
auto *pData = DecoratorPrivate::get<ClassInfoPrivate>(self);
- PyObject *key;
- PyObject *value;
+ PyObject *key{};
+ PyObject *value{};
Py_ssize_t pos = 0;
// PyDict_Next causes a segfault if kwds is empty
if (PyDict_Size(infoDict) > 0) {
while (PyDict_Next(infoDict, &pos, &key, &value)) {
if (Shiboken::String::check(key) && Shiboken::String::check(value)) {
- pData->m_data[Shiboken::String::toCString(key)] = Shiboken::String::toCString(value);
+ ClassInfo info{Shiboken::String::toCString(key),
+ Shiboken::String::toCString(value)};
+ pData->m_data.append(info);
} else {
PyErr_SetString(PyExc_TypeError, "All keys and values provided to ClassInfo() "
"must be strings");
}
}
- return PyErr_Occurred() ? -1 : 0;
+ return PyErr_Occurred() != nullptr ? -1 : 0;
}
static const char *ClassInfo_SignatureStrings[] = {
bool checkType(PyObject *pyObj)
{
- if (pyObj)
- return PyType_IsSubtype(Py_TYPE(pyObj), PySideClassInfo_TypeF());
- return false;
+ return pyObj != nullptr
+ && PyType_IsSubtype(Py_TYPE(pyObj), PySideClassInfo_TypeF()) != 0;
}
-QMap<QByteArray, QByteArray> getMap(PyObject *obj)
+ClassInfoList getClassInfoList(PyObject *decorator)
{
- auto *pData = PySide::ClassDecorator::DecoratorPrivate::get<ClassInfoPrivate>(obj);
+ auto *pData = PySide::ClassDecorator::DecoratorPrivate::get<ClassInfoPrivate>(decorator);
return pData->m_data;
}
-} //namespace PySide::Property
+bool setClassInfo(PyTypeObject *type, const QByteArray &key,
+ const QByteArray &value)
+{
+ auto *userData = PySide::retrieveTypeUserData(type);
+ const bool result = userData != nullptr;
+ if (result) {
+ PySide::MetaObjectBuilder &mo = userData->mo;
+ mo.addInfo(key, value);
+ }
+ return result;
+}
+
+bool setClassInfo(PyTypeObject *type, const ClassInfoList &list)
+{
+ auto *userData = PySide::retrieveTypeUserData(type);
+ const bool result = userData != nullptr;
+ if (result) {
+ PySide::MetaObjectBuilder &mo = userData->mo;
+ for (const auto &info : list)
+ mo.addInfo(info.key.constData(), info.value.constData());
+ }
+ return result;
+}
+
+} //namespace PySide::ClassInfo
#include <sbkpython.h>
-#include <QtCore/QMap>
#include <QtCore/QByteArray>
+#include <QtCore/QList>
namespace PySide::ClassInfo {
+struct ClassInfo
+{
+ QByteArray key;
+ QByteArray value;
+};
+
+using ClassInfoList = QList<ClassInfo>;
+
PYSIDE_API bool checkType(PyObject* pyObj);
-PYSIDE_API QMap<QByteArray, QByteArray> getMap(PyObject *obj);
+PYSIDE_API ClassInfoList getClassInfoList(PyObject *decorator);
+
+PYSIDE_API bool setClassInfo(PyTypeObject *type, const QByteArray &key,
+ const QByteArray &value);
+PYSIDE_API bool setClassInfo(PyTypeObject *type, const ClassInfoList &list);
} // namespace PySide::ClassInfo
int tp_init(PyObject *self, PyObject *args, PyObject *kwds) override;
const char *name() const override;
- QMap<QByteArray, QByteArray> m_data;
+ ClassInfoList m_data;
bool m_alreadyWrapped = false;
};
#include <sbkpython.h>
-#include <QtCore/QtGlobal>
+#include <QtCore/qtconfigmacros.h>
QT_BEGIN_NAMESPACE
class QObject;
#include <pysidemacros.h>
-#include <QtCore/QtGlobal>
+#include <QtCore/qtconfigmacros.h>
QT_FORWARD_DECLARE_CLASS(QMetaType)
#include "pysidesignal_p.h"
#include <shiboken.h>
+#include <pep384ext.h>
#include <signature.h>
using namespace Shiboken;
static PyObject *qpropertyTpNew(PyTypeObject *subtype, PyObject * /* args */, PyObject * /* kwds */)
{
- auto *me = reinterpret_cast<PySideProperty *>(subtype->tp_alloc(subtype, 0));
+ auto *me = PepExt_TypeCallAlloc<PySideProperty>(subtype, 0);
me->d = new PySidePropertyPrivate;
return reinterpret_cast<PyObject *>(me);
}
Py_DECREF(Py_TYPE(self));
}
PyObject_GC_UnTrack(self);
- Py_TYPE(self)->tp_free(self);
+ PepExt_TypeCallFree(self);
}
// Create a copy of the property to prevent the @property.setter from modifying
"PySide6.QtCore.Property.read(self,fget:typing.Callable)->PySide6.QtCore.Property",
"PySide6.QtCore.Property.setter(self,fset:typing.Callable)->PySide6.QtCore.Property",
"PySide6.QtCore.Property.write(self,fset:typing.Callable)->PySide6.QtCore.Property",
+ "PySide6.QtCore.Property.__call__(self, func:typing.Callable)->PySide6.QtCore.Property",
nullptr}; // Sentinel
void init(PyObject *module)
/// Hash function used to enable hash on objects not supported by the native Qt
/// library which have a toString() function.
template<class T>
-inline Py_ssize_t hash(const T& value)
+[[deprecated]] inline Py_ssize_t hash(const T& value)
{
return qHash(value.toString());
}
#include <pysidemacros.h>
-#include <QtCore/QtGlobal>
+#include <QtCore/qtclasshelpermacros.h>
+
+#include <cstddef>
QT_FORWARD_DECLARE_CLASS(QObject)
QT_FORWARD_DECLARE_STRUCT(QMetaObject)
#include <QtCore/QObject>
#include <QtCore/QMetaMethod>
#include <QtCore/QMetaObject>
+#include <pep384ext.h>
#include <signature.h>
#include <algorithm>
Py_XDECREF(self->homonymousMethod);
self->homonymousMethod = nullptr;
- Py_TYPE(pySelf)->tp_base->tp_free(self);
+ PepExt_TypeCallFree(Py_TYPE(pySelf)->tp_base, self);
}
static PyObject *signalGetItem(PyObject *obSelf, PyObject *key)
self->d = nullptr;
}
self->deleted = true;
- Py_TYPE(pySelf)->tp_base->tp_free(self);
+ PepExt_TypeCallFree(Py_TYPE(pySelf)->tp_base, self);
}
// PYSIDE-1523: PyFunction_Check is not accepting compiled functions and
return ret;
}
-static int argCount(const FunctionArgumentsResult &args)
+struct ArgCount
+{
+ int min;
+ int max;
+};
+
+// Return a pair of minimum / arg count "foo(p1, p2=0)" -> {1, 2}
+ArgCount argCount(const FunctionArgumentsResult &args)
{
Q_ASSERT(args.objCode);
- return (PepCode_GET_FLAGS(args.objCode) & CO_VARARGS) != 0
- ? -1 : PepCode_GET_ARGCOUNT(args.objCode);
+ ArgCount result{-1, -1};
+ if ((PepCode_GET_FLAGS(args.objCode) & CO_VARARGS) == 0) {
+ result.min = result.max = PepCode_GET_ARGCOUNT(args.objCode);
+ if (args.function != nullptr) {
+ if (auto *defaultArgs = PepFunction_GetDefaults(args.function))
+ result.min -= PyTuple_Size(defaultArgs);
+ }
+ }
+ return result;
+}
+
+// Find Signal Instance for argument count.
+static PySideSignalInstance *findSignalInstance(PySideSignalInstance *source, int argCount)
+{
+ for (auto *si = source; si != nullptr; si = si->d->next) {
+ if (si->d->argCount == argCount)
+ return si;
+ }
+ return nullptr;
}
static PyObject *signalInstanceConnect(PyObject *self, PyObject *args, PyObject *kwds)
return nullptr;
PySideSignalInstance *source = reinterpret_cast<PySideSignalInstance *>(self);
- if (!source->d) {
- PyErr_Format(PyExc_RuntimeError, "cannot connect uninitialized SignalInstance");
- return nullptr;
- }
- if (source->deleted) {
- PyErr_Format(PyExc_RuntimeError, "Signal source has been deleted");
- return nullptr;
- }
+ if (!source->d)
+ return PyErr_Format(PyExc_RuntimeError, "cannot connect uninitialized SignalInstance");
+ if (source->deleted)
+ return PyErr_Format(PyExc_RuntimeError, "Signal source has been deleted");
Shiboken::AutoDecRef pyArgs(PyList_New(0));
}
} else {
// Check signature of the slot (method or function) to match signal
- bool matchedSlot = false;
-
- PySideSignalInstance *it = source;
const auto args = extractFunctionArgumentsFromSlot(slot);
+ PySideSignalInstance *matchedSlot = nullptr;
if (args.function != nullptr) {
- qsizetype slotArgs = argCount(args);
- if (args.isMethod)
- slotArgs -= 1;
+ auto slotArgRange = argCount(args);
+ if (args.isMethod) {
+ slotArgRange.min -= 1;
+ slotArgRange.max -= 1;
+ }
// Get signature args
- bool isShortCircuit = false;
- QStringList argsSignature = PySide::Signal::getArgsFromSignature(it->d->signature,
- &isShortCircuit);
- qsizetype signatureArgs = argsSignature.size();
-
// Iterate the possible types of connection for this signal and compare
// it with slot arguments
- if (signatureArgs != slotArgs) {
- while (it->d->next != nullptr) {
- it = it->d->next;
- argsSignature = PySide::Signal::getArgsFromSignature(it->d->signature,
- &isShortCircuit);
- signatureArgs = argsSignature.size();
- if (signatureArgs == slotArgs) {
- matchedSlot = true;
- break;
- }
- }
+ for (int slotArgs = slotArgRange.max;
+ slotArgs >= slotArgRange.min && matchedSlot == nullptr; --slotArgs) {
+ matchedSlot = findSignalInstance(source, slotArgs);
}
}
// Adding references to pyArgs
PyList_Append(pyArgs, source->d->source);
- if (matchedSlot) {
+ if (matchedSlot != nullptr) {
// If a slot matching the same number of arguments was found,
// include signature to the pyArgs
- Shiboken::AutoDecRef signature(PySide::Signal::buildQtCompatible(it->d->signature));
+ Shiboken::AutoDecRef signature(PySide::Signal::buildQtCompatible(matchedSlot->d->signature));
PyList_Append(pyArgs, signature);
} else {
// Try the first by default if the slot was not found
Shiboken::AutoDecRef tupleArgs(PyList_AsTuple(pyArgs));
Shiboken::AutoDecRef pyMethod(PyObject_GetAttr(source->d->source,
PySide::PySideName::qtConnect()));
- if (pyMethod.isNull()) { // PYSIDE-79: check if pyMethod exists.
- PyErr_SetString(PyExc_RuntimeError, "method 'connect' vanished!");
- return nullptr;
- }
+ if (pyMethod.isNull()) // PYSIDE-79: check if pyMethod exists.
+ return PyErr_Format(PyExc_RuntimeError, "method 'connect' vanished!");
PyObject *result = PyObject_CallObject(pyMethod, tupleArgs);
if (connection_Check(result))
return result;
static PyObject *signalInstanceEmit(PyObject *self, PyObject *args)
{
PySideSignalInstance *source = reinterpret_cast<PySideSignalInstance *>(self);
- if (!source->d) {
- PyErr_Format(PyExc_RuntimeError, "cannot emit uninitialized SignalInstance");
- return nullptr;
- }
+ if (!source->d)
+ return PyErr_Format(PyExc_RuntimeError, "cannot emit uninitialized SignalInstance");
// PYSIDE-2201: Check if the object has vanished meanwhile.
// Tried to revive it without exception, but this gives problems.
- if (source->deleted) {
- PyErr_Format(PyExc_RuntimeError, "The SignalInstance object was already deleted");
- return nullptr;
- }
+ if (source->deleted)
+ return PyErr_Format(PyExc_RuntimeError, "The SignalInstance object was already deleted");
Shiboken::AutoDecRef pyArgs(PyList_New(0));
int numArgsGiven = PySequence_Fast_GET_SIZE(args);
message += '"' + data->d->signature + '"';
}
- PyErr_SetString(PyExc_IndexError, message.constData());
- return nullptr;
+ return PyErr_Format(PyExc_IndexError, message.constData());
+}
+
+static inline void warnDisconnectFailed(PyObject *aSlot, const QByteArray &signature)
+{
+ if (PyErr_Occurred() != nullptr) { // avoid "%S" invoking str() when an error is set.
+ PyErr_WarnFormat(PyExc_RuntimeWarning, 0, "Failed to disconnect (%s) from signal \"%s\".",
+ Py_TYPE(aSlot)->tp_name, signature.constData());
+ } else {
+ PyErr_WarnFormat(PyExc_RuntimeWarning, 0, "Failed to disconnect (%S) from signal \"%s\".",
+ aSlot, signature.constData());
+ }
}
static PyObject *signalInstanceDisconnect(PyObject *self, PyObject *args)
{
auto source = reinterpret_cast<PySideSignalInstance *>(self);
- if (!source->d) {
- PyErr_Format(PyExc_RuntimeError, "cannot disconnect uninitialized SignalInstance");
- return nullptr;
- }
+ if (!source->d)
+ return PyErr_Format(PyExc_RuntimeError, "cannot disconnect uninitialized SignalInstance");
+
Shiboken::AutoDecRef pyArgs(PyList_New(0));
PyObject *slot = Py_None;
Shiboken::AutoDecRef pyMethod(PyObject_GetAttr(source->d->source,
PySide::PySideName::qtDisconnect()));
PyObject *result = PyObject_CallObject(pyMethod, tupleArgs);
- if (!result || result == Py_True)
- return result;
- Py_DECREF(result);
+ if (result != Py_True)
+ warnDisconnectFailed(slot, source->d->signature);
+ return result;
}
- PyErr_Format(PyExc_RuntimeError, "Failed to disconnect (%S) from signal \"%s\".",
- slot, source->d->signature.constData());
- return nullptr;
+ warnDisconnectFailed(slot, source->d->signature);
+ Py_RETURN_FALSE;
}
// PYSIDE-68: Supply the missing __get__ function
// The only way calling a signal can succeed (the Python equivalent of C++'s operator() )
// is when a method with the same name as the signal is attached to an object.
// An example is QProcess::error() (don't check the docs, but the source code of qprocess.h).
- if (!signal->homonymousMethod) {
- PyErr_SetString(PyExc_TypeError, "native Qt signal is not callable");
- return nullptr;
- }
-
- descrgetfunc getDescriptor = Py_TYPE(signal->homonymousMethod)->tp_descr_get;
+ if (!signal->homonymousMethod)
+ return PyErr_Format(PyExc_TypeError, "native Qt signal is not callable");
// Check if there exists a method with the same name as the signal, which is also a static
// method in C++ land.
- Shiboken::AutoDecRef homonymousMethod(getDescriptor(signal->homonymousMethod,
- nullptr, nullptr));
+ Shiboken::AutoDecRef homonymousMethod(PepExt_Type_CallDescrGet(signal->homonymousMethod,
+ nullptr, nullptr));
if (PyCFunction_Check(homonymousMethod.object())
&& (PyCFunction_GET_FLAGS(homonymousMethod.object()) & METH_STATIC))
return PyObject_Call(homonymousMethod, args, kw);
// Assumes homonymousMethod is not a static method.
- ternaryfunc callFunc = Py_TYPE(signal->homonymousMethod)->tp_call;
+ ternaryfunc callFunc = PepExt_Type_GetCallSlot(Py_TYPE(signal->homonymousMethod));
return callFunc(homonymousMethod, args, kw);
}
return nullptr;
}
- descrgetfunc getDescriptor = Py_TYPE(hom)->tp_descr_get;
- Shiboken::AutoDecRef homonymousMethod(getDescriptor(hom, PySideSignal->d->source, nullptr));
+ Shiboken::AutoDecRef homonymousMethod(PepExt_Type_CallDescrGet(hom, PySideSignal->d->source,
+ nullptr));
return PyObject_Call(homonymousMethod, args, kw);
}
"PySide6.QtCore.SignalInstance.connect(self,slot:object,"
"type:PySide6.QtCore.Qt.ConnectionType=PySide6.QtCore.Qt.ConnectionType.AutoConnection)"
"->PySide6.QtCore.QMetaObject.Connection",
- "PySide6.QtCore.SignalInstance.disconnect(self,slot:object=nullptr)",
+ "PySide6.QtCore.SignalInstance.disconnect(self,slot:object=nullptr)->bool",
"PySide6.QtCore.SignalInstance.emit(self,*args:typing.Any)",
nullptr}; // Sentinel
static PySideSignalData::Signature parseSignature(PyObject *args)
{
- PySideSignalData::Signature result{{}, QMetaMethod::Compatibility};
+ PySideSignalData::Signature result{{}, QMetaMethod::Compatibility, 0};
if (args && (Shiboken::String::check(args) || !PyTuple_Check(args))) {
result.signature = getTypeName(args);
+ result.argCount = 1;
return result;
}
if (!result.signature.isEmpty())
result.signature += ',';
result.signature += typeName;
+ ++result.argCount;
}
}
return result;
selfPvt->source = source;
const auto &signature = signal->data->signatures.at(index);
selfPvt->signature = buildSignature(self->d->signalName, signature.signature);
+ selfPvt->argCount = signature.argCount;
selfPvt->attributes = signature.attributes;
selfPvt->homonymousMethod = nullptr;
if (signal->homonymousMethod) {
// separate SignalName
selfPvt->signalName = cppName;
selfPvt->signature = m.methodSignature();
+ selfPvt->argCount = int(m.parameterCount());
selfPvt->attributes = m.attributes();
selfPvt->homonymousMethod = nullptr;
selfPvt->next = nullptr;
void registerSignals(PyTypeObject *pyObj, const QMetaObject *metaObject)
{
using Signature = PySideSignalData::Signature;
- using SignalSigMap = QHash<QByteArray, QList<Signature>>;
- SignalSigMap signalsFound;
+ struct MetaSignal
+ {
+ QByteArray methodName;
+ QList<Signature> signatures;
+ };
+
+ QList<MetaSignal> signalsFound;
for (int i = metaObject->methodOffset(), max = metaObject->methodCount(); i < max; ++i) {
QMetaMethod method = metaObject->method(i);
if (method.methodType() == QMetaMethod::Signal) {
QByteArray methodName(method.methodSignature());
- methodName.chop(methodName.size() - methodName.indexOf('('));
- Signature signature{method.parameterTypes().join(','), {}};
+ methodName.truncate(methodName.indexOf('('));
+ Signature signature{method.parameterTypes().join(','), {},
+ short(method.parameterCount())};
if (method.attributes() & QMetaMethod::Cloned)
signature.attributes = QMetaMethod::Cloned;
- signalsFound[methodName] << signature;
+ auto it = std::find_if(signalsFound.begin(), signalsFound.end(),
+ [methodName](const MetaSignal &ms)
+ { return ms.methodName == methodName; });
+ if (it != signalsFound.end())
+ it->signatures << signature;
+ else
+ signalsFound.append(MetaSignal{methodName, {signature}});
}
}
- SignalSigMap::Iterator it = signalsFound.begin();
- SignalSigMap::Iterator end = signalsFound.end();
- for (; it != end; ++it) {
+ for (const auto &metaSignal : std::as_const(signalsFound)) {
PySideSignal *self = PyObject_New(PySideSignal, PySideSignal_TypeF());
self->data = new PySideSignalData;
- self->data->signalName = it.key();
+ self->data->signalName = metaSignal.methodName;
self->homonymousMethod = nullptr;
// Empty signatures comes first! So they will be the default signal signature
- self->data->signatures = it.value();
+ self->data->signatures = metaSignal.signatures;
std::stable_sort(self->data->signatures.begin(),
self->data->signatures.end(), &compareSignals);
- _addSignalToWrapper(pyObj, it.key(), self);
+ _addSignalToWrapper(pyObj, metaSignal.methodName, self);
Py_DECREF(reinterpret_cast<PyObject *>(self));
}
}
return result;
}
-QStringList getArgsFromSignature(const char *signature, bool *isShortCircuit)
+QByteArrayList getArgsFromSignature(const char *signature, bool *isShortCircuit)
{
- QString qsignature = QString::fromLatin1(signature).trimmed();
- QStringList result;
+ QByteArray qsignature = QByteArray(signature).trimmed();
+ QByteArrayList result;
if (isShortCircuit)
*isShortCircuit = !qsignature.contains(u'(');
- if (qsignature.contains(u"()") || qsignature.contains(u"(void)"))
+ if (qsignature.contains("()") || qsignature.contains("(void)"))
return result;
- if (qsignature.endsWith(u')')) {
- const int paren = qsignature.indexOf(u'(');
+ if (qsignature.endsWith(')')) {
+ const auto paren = qsignature.indexOf('(');
if (paren >= 0) {
qsignature.chop(1);
qsignature.remove(0, paren + 1);
result = qsignature.split(u',');
- for (QString &type : result)
+ for (auto &type : result)
type = type.trimmed();
}
}
return result;
}
-QString getCallbackSignature(const char *signal, QObject *receiver, PyObject *callback, bool encodeName)
+QByteArray getCallbackSignature(const char *signal, QObject *receiver,
+ PyObject *callback, bool encodeName)
{
QByteArray functionName;
qsizetype numArgs = -1;
qsizetype useSelf = slotArgs.isMethod ? 1 : 0;
if (slotArgs.function != nullptr) {
- numArgs = argCount(slotArgs);
+ numArgs = argCount(slotArgs).max;
#ifdef PYPY_VERSION
} else if (Py_TYPE(callback) == PepBuiltinMethod_TypePtr) {
// PYSIDE-535: PyPy has a special builtin method that acts almost like PyCFunction.
functionName[0] = '_';
functionName[functionName.size() - 1] = '_';
}
- const QString functionNameS = QLatin1String(functionName);
- QString signature = encodeName ? codeCallbackName(callback, functionNameS) : functionNameS;
- QStringList args = getArgsFromSignature(signal, &isShortCircuit);
+ QByteArray signature = encodeName ? codeCallbackName(callback, functionName) : functionName;
+ QByteArrayList args = getArgsFromSignature(signal, &isShortCircuit);
if (!isShortCircuit) {
signature.append(u'(');
numArgs = std::numeric_limits<qsizetype>::max();
while (!args.isEmpty() && (args.size() > (numArgs - useSelf)))
args.removeLast();
- signature.append(args.join(u','));
- signature.append(u')');
+ signature.append(args.join(','));
+ signature.append(')');
}
return signature;
}
return true;
}
-QString codeCallbackName(PyObject *callback, const QString &funcName)
+QByteArray codeCallbackName(PyObject *callback, const QByteArray &funcName)
{
if (PyMethod_Check(callback)) {
PyObject *self = PyMethod_GET_SELF(callback);
PyObject *func = PyMethod_GET_FUNCTION(callback);
- return funcName + QString::number(quint64(self), 16) + QString::number(quint64(func), 16);
+ return funcName + QByteArray::number(quint64(self), 16) + QByteArray::number(quint64(func), 16);
}
// PYSIDE-1523: Handle the compiled case.
if (PySide::isCompiledMethod(callback)) {
// Not retaining references inline with what PyMethod_GET_(SELF|FUNC) does.
Shiboken::AutoDecRef self(PyObject_GetAttr(callback, PySide::PySideName::im_self()));
Shiboken::AutoDecRef func(PyObject_GetAttr(callback, PySide::PySideName::im_func()));
- return funcName + QString::number(quint64(self), 16) + QString::number(quint64(func), 16);
+ return funcName + QByteArray::number(quint64(self), 16) + QByteArray::number(quint64(func), 16);
}
- return funcName + QString::number(quint64(callback), 16);
+ return funcName + QByteArray::number(quint64(callback), 16);
}
QByteArray voidType()
* @param encodeName Used to specify if the returned signature will be encoded with Qt signal/slot style
* @return Return the callback signature
**/
-PYSIDE_API QString getCallbackSignature(const char *signal, QObject *receiver, PyObject *callback, bool encodeName);
+PYSIDE_API QByteArray getCallbackSignature(const char *signal, QObject *receiver,
+ PyObject *callback, bool encodeName);
/**
* This function parses the signature and then returns a list of argument types.
* @return Return true if this is a Qt Signal, otherwise return false
* @todo replace return type by QList<QByteArray>
**/
-QStringList getArgsFromSignature(const char *signature,
- bool *isShortCircuit = nullptr);
+QByteArrayList getArgsFromSignature(const char *signature,
+ bool *isShortCircuit = nullptr);
} // namespace PySide::Signal
{
QByteArray signature; // ','-separated list of parameter types
unsigned short attributes;
+ short argCount;
};
QByteArray signalName;
PyObject *homonymousMethod = nullptr;
PySideSignalInstance *next = nullptr;
unsigned short attributes = 0;
+ short argCount = 0;
};
namespace PySide::Signal {
void init(PyObject *module);
bool connect(PyObject *source, const char *signal, PyObject *callback);
QByteArray getTypeName(PyObject *);
- QString codeCallbackName(PyObject *callback, const QString &funcName);
+ QByteArray codeCallbackName(PyObject *callback, const QByteArray &funcName);
QByteArray voidType();
} // namespace PySide::Signal
STATIC_STRING_IMPL(im_func, "im_func")
STATIC_STRING_IMPL(im_self, "im_self")
STATIC_STRING_IMPL(name, "name")
+STATIC_STRING_IMPL(orig_dict, "orig_dict")
STATIC_STRING_IMPL(parameters, "parameters")
STATIC_STRING_IMPL(property, "property")
STATIC_STRING_IMPL(select_id, "select_id")
PYSIDE_API PyObject *im_func();
PYSIDE_API PyObject *im_self();
PYSIDE_API PyObject *name();
+PYSIDE_API PyObject *orig_dict();
PYSIDE_API PyObject *parameters();
PYSIDE_API PyObject *property();
PYSIDE_API PyObject *select_id();
#include <pysidemacros.h>
-#include <QtCore/QtGlobal>
+#include <QtCore/qtclasshelpermacros.h>
QT_FORWARD_DECLARE_CLASS(QDebug)
QT_FORWARD_DECLARE_CLASS(QString)
PYSIDE_API QDebug operator<<(QDebug debug, const debugPyObject &o);
+struct debugPyBuffer
+{
+ PYSIDE_API explicit debugPyBuffer(Py_buffer *b) noexcept;
+
+ Py_buffer *m_buffer;
+};
+
+PYSIDE_API QDebug operator<<(QDebug debug, const debugPyBuffer &b);
+
} //namespace PySide
#endif // PYSIDESTRING_H
if (!result.usingGlobalReceiver && result.receiver && result.self) {
result.callbackSig =
PySide::Signal::getCallbackSignature(signal, result.receiver, callback,
- result.usingGlobalReceiver).toLatin1();
+ result.usingGlobalReceiver);
const QMetaObject *metaObject = result.receiver->metaObject();
result.slotIndex = metaObject->indexOfSlot(result.callbackSig.constData());
if (PyMethod_Check(callback) != 0 && result.slotIndex != -1
result.receiver->moveToThread(receiverThread);
result.callbackSig =
PySide::Signal::getCallbackSignature(signal, result.receiver, callback,
- result.usingGlobalReceiver).toLatin1();
+ result.usingGlobalReceiver);
const QMetaObject *metaObject = result.receiver->metaObject();
result.slotIndex = metaObject->indexOfSlot(result.callbackSig.constData());
}
#include <sbkstaticstrings.h>
#include <sbkerrors.h>
+#include <QtCore/QCoreApplication>
#include <QtCore/QByteArrayView>
#include <QtCore/QDebug>
#include <QtCore/QHash>
#include <QtCore/QScopedPointer>
+#include <QtCore/QTimerEvent>
#include <algorithm>
#include <limits>
#include <memory>
+using namespace Qt::StringLiterals;
+
#if QSLOT_CODE != 1 || QSIGNAL_CODE != 2
#error QSLOT_CODE and/or QSIGNAL_CODE changed! change the hardcoded stuff to the correct value!
#endif
#include "globalreceiverv2.h"
static PyObject *metaObjectAttr = nullptr;
-static PyObject *parseArguments(const QList< QByteArray >& paramTypes, void **args);
+static PyObject *parseArguments(const QMetaMethod &method, void **args);
static bool emitShortCircuitSignal(QObject *source, int signalIndex, PyObject *args);
+
+static bool qAppRunning = false;
+
static void destroyMetaObject(PyObject *obj)
{
void *ptr = PyCapsule_GetPointer(obj, nullptr);
return it != mapping.constEnd() ? it.value() : "<Unknown>";
}
+static QByteArray methodSignature(const QMetaMethod &method)
+{
+ QByteArray result;
+ if (auto *t = method.typeName()) {
+ result += t;
+ result += ' ';
+ }
+ result += method.methodSignature();
+ return result;
+}
+
+static QByteArray msgCannotConvertParameter(const QMetaMethod &method, qsizetype p)
+{
+ return "Cannot call meta function \""_ba + methodSignature(method)
+ + "\" because parameter " + QByteArray::number(p) + " of type \""_ba
+ + method.parameterTypeName(p) + "\" cannot be converted."_ba;
+}
+
+static QByteArray msgCannotConvertReturn(const QMetaMethod &method)
+{
+ return "The return value of \""_ba + methodSignature(method) + "\" cannot be converted."_ba;
+}
+
namespace PySide {
PyObjectWrapper::PyObjectWrapper()
using namespace PySide;
+// Listen for destroy() of main thread objects and ensure cleanup
+class SignalManagerDestroyListener : public QObject
+{
+ Q_OBJECT
+public:
+ Q_DISABLE_COPY_MOVE(SignalManagerDestroyListener)
+
+ using QObject::QObject;
+
+public Q_SLOTS:
+ void destroyNotify(const QObject *);
+
+protected:
+ void timerEvent(QTimerEvent *event) override;
+
+private:
+ int m_timerId = -1;
+};
+
+void SignalManagerDestroyListener::destroyNotify(const QObject *)
+{
+ if (qAppRunning && m_timerId == -1)
+ m_timerId = startTimer(0);
+}
+
+void SignalManagerDestroyListener::timerEvent(QTimerEvent *event)
+{
+ if (event->timerId() == m_timerId) {
+ killTimer(std::exchange(m_timerId, -1));
+ SignalManager::instance().purgeEmptyGlobalReceivers();
+ }
+}
+
struct SignalManager::SignalManagerPrivate
{
Q_DISABLE_COPY_MOVE(SignalManagerPrivate)
SignalManagerPrivate() noexcept = default;
~SignalManagerPrivate() { clear(); }
- void deleteGobalReceiver(const QObject *gr);
+ void deleteGlobalReceiver(const QObject *gr);
void clear();
- void purgeEmptyGobalReceivers();
+ void purgeEmptyGlobalReceivers();
GlobalReceiverV2Map m_globalReceivers;
static SignalManager::QmlMetaCallErrorHandler m_qmlMetaCallErrorHandler;
static int qtPropertyMetacall(QObject *object, QMetaObject::Call call,
int id, void **args);
static int qtMethodMetacall(QObject *object, int id, void **args);
+
+ QPointer<SignalManagerDestroyListener> m_listener;
};
SignalManager::QmlMetaCallErrorHandler
SignalManagerPrivate::m_qmlMetaCallErrorHandler = handler;
}
+static void qAppAboutToQuit()
+{
+ qAppRunning = false;
+ SignalManager::instance().purgeEmptyGlobalReceivers();
+}
+
+static bool isInMainThread(const QObject *o)
+{
+ if (o->isWidgetType() || o->isWindowType() || o->isQuickItemType())
+ return true;
+ auto *app = QCoreApplication::instance();
+ return app != nullptr && app->thread() == o->thread();
+}
+
QObject *SignalManager::globalReceiver(QObject *sender, PyObject *callback, QObject *receiver)
{
+ if (m_d->m_listener.isNull() && !QCoreApplication::closingDown()) {
+ if (auto *app = QCoreApplication::instance()) {
+ // The signal manager potentially outlives QCoreApplication, ensure deletion
+ m_d->m_listener = new SignalManagerDestroyListener(app);
+ m_d->m_listener->setObjectName("qt_pyside_signalmanagerdestroylistener");
+ QObject::connect(app, &QCoreApplication::aboutToQuit, qAppAboutToQuit);
+ qAppRunning = true;
+ }
+ }
+
auto &globalReceivers = m_d->m_globalReceivers;
const GlobalReceiverKey key = GlobalReceiverV2::key(callback);
auto it = globalReceivers.find(key);
auto gr = std::make_shared<GlobalReceiverV2>(callback, receiver);
it = globalReceivers.insert(key, gr);
}
- if (sender)
+
+ if (sender != nullptr) {
it.value()->incRef(sender); // create a link reference
+ // For main thread-objects, add a notification for destroy (PYSIDE-2646, 2141)
+ if (qAppRunning && !m_d->m_listener.isNull() && isInMainThread(sender)) {
+ QObject::connect(sender, &QObject::destroyed,
+ m_d->m_listener, &SignalManagerDestroyListener::destroyNotify,
+ Qt::UniqueConnection);
+ }
+ }
+
return it.value().get();
}
+void SignalManager::purgeEmptyGlobalReceivers()
+{
+ m_d->purgeEmptyGlobalReceivers();
+}
+
void SignalManager::notifyGlobalReceiver(QObject *receiver)
{
reinterpret_cast<GlobalReceiverV2 *>(receiver)->notify();
- m_d->purgeEmptyGobalReceivers();
+ purgeEmptyGlobalReceivers();
}
void SignalManager::releaseGlobalReceiver(const QObject *source, QObject *receiver)
auto gr = static_cast<GlobalReceiverV2 *>(receiver);
gr->decRef(source);
if (gr->isEmpty())
- m_d->deleteGobalReceiver(gr);
+ m_d->deleteGlobalReceiver(gr);
}
-void SignalManager::deleteGobalReceiver(const QObject *gr)
+void SignalManager::deleteGlobalReceiver(const QObject *gr)
{
- SignalManager::instance().m_d->deleteGobalReceiver(gr);
+ SignalManager::instance().m_d->deleteGlobalReceiver(gr);
}
-void SignalManager::SignalManagerPrivate::deleteGobalReceiver(const QObject *gr)
+void SignalManager::SignalManagerPrivate::deleteGlobalReceiver(const QObject *gr)
{
for (auto it = m_globalReceivers.begin(), end = m_globalReceivers.end(); it != end; ++it) {
if (it.value().get() == gr) {
return g->isEmpty();
}
-void SignalManager::SignalManagerPrivate::purgeEmptyGobalReceivers()
+void SignalManager::SignalManagerPrivate::purgeEmptyGlobalReceivers()
{
// Delete repetitively (see comment in clear()).
while (true) {
Q_ASSERT(pyMethod);
Shiboken::GilState gil;
- PyObject *pyArguments = nullptr;
-
- if (isShortCuit){
- pyArguments = reinterpret_cast<PyObject *>(args[1]);
- } else {
- pyArguments = parseArguments(method.parameterTypes(), args);
- }
+ PyObject *pyArguments = isShortCuit
+ ? reinterpret_cast<PyObject *>(args[1]) : parseArguments(method, args);
if (pyArguments) {
QScopedPointer<Shiboken::Conversions::SpecificConverter> retConverter;
const char *returnType = method.typeName();
- if (returnType && std::strcmp("", returnType) && std::strcmp("void", returnType)) {
+ if (returnType != nullptr && returnType[0] != 0 && std::strcmp("void", returnType) != 0) {
retConverter.reset(new Shiboken::Conversions::SpecificConverter(returnType));
if (!retConverter->isValid()) {
- PyErr_Format(PyExc_RuntimeError, "Can't find converter for '%s' to call Python meta method.", returnType);
+ PyErr_SetString(PyExc_RuntimeError, msgCannotConvertReturn(method).constData());
return -1;
}
}
Shiboken::AutoDecRef retval(PyObject_CallObject(pyMethod, pyArguments));
- if (!isShortCuit && pyArguments){
+ if (!isShortCuit && pyArguments)
Py_DECREF(pyArguments);
- }
- if (!retval.isNull() && retval != Py_None && !PyErr_Occurred() && retConverter) {
+ if (!retval.isNull() && retval != Py_None && !PyErr_Occurred() && retConverter)
retConverter->toCpp(retval, args[0]);
- }
}
return -1;
return builder->update();
}
-static PyObject *parseArguments(const QList<QByteArray>& paramTypes, void **args)
+static PyObject *parseArguments(const QMetaMethod &method, void **args)
{
+ const auto ¶mTypes = method.parameterTypes();
const qsizetype argsSize = paramTypes.size();
PyObject *preparedArgs = PyTuple_New(argsSize);
for (qsizetype i = 0; i < argsSize; ++i) {
void *data = args[i+1];
- const char *dataType = paramTypes[i].constData();
- Shiboken::Conversions::SpecificConverter converter(dataType);
- if (converter) {
- PyTuple_SET_ITEM(preparedArgs, i, converter.toPython(data));
- } else {
- PyErr_Format(PyExc_TypeError, "Can't call meta function because I have no idea how to handle %s", dataType);
+ auto param = paramTypes.at(i);
+ Shiboken::Conversions::SpecificConverter converter(param.constData());
+ if (!converter) {
+ PyErr_SetString(PyExc_TypeError, msgCannotConvertParameter(method, i).constData());
Py_DECREF(preparedArgs);
return nullptr;
}
+ PyTuple_SET_ITEM(preparedArgs, i, converter.toPython(data));
}
return preparedArgs;
}
source->qt_metacall(QMetaObject::InvokeMetaMethod, signalIndex, signalArgs);
return true;
}
+
+#include "signalmanager.moc"
// Disconnect all signals managed by Globalreceiver
void clear();
+ void purgeEmptyGlobalReceivers();
// Utility function to call a python method usign args received in qt_metacall
static int callPythonMetaMethod(const QMetaMethod& method, void** args, PyObject* obj, bool isShortCuit);
- static void deleteGobalReceiver(const QObject *globalReceiver);
+ static void deleteGlobalReceiver(const QObject *globalReceiver);
private:
struct SignalManagerPrivate;
#include <signature.h>
#include <sbkstring.h>
-#include <QtCore/QtGlobal>
#include <QtQml/qqml.h>
#include <algorithm>
#include "pysideqmlmacros.h"
-#include <QtCore/QtGlobal>
+#include <QtCore/qtconfigmacros.h>
QT_FORWARD_DECLARE_CLASS(QObject)
#include <signature.h>
#include <sbkstring.h>
-#include <QtCore/QtGlobal>
#include <QtQml/qqml.h>
// The QmlExtended decorator modifies QmlElement to register an extension.
#include <signature.h>
#include <sbkstring.h>
-#include <QtCore/QtGlobal>
#include <QtCore/QDebug>
// The QmlForeign decorator modifies QmlElement to create a different type
#include "pysideqmlregistertype_p.h"
#include <shiboken.h>
+#include <pep384ext.h>
#include <signature.h>
#include <pysideproperty.h>
static PyObject *propList_tp_new(PyTypeObject *subtype, PyObject * /* args */, PyObject * /* kwds */)
{
- PySideProperty *me = reinterpret_cast<PySideProperty *>(subtype->tp_alloc(subtype, 0));
+ auto *me = PepExt_TypeCallAlloc<PySideProperty>(subtype, 0);
me->d = new QmlListPropertyPrivate;
return reinterpret_cast<PyObject *>(me);
}
return 0;
}
- int cppResult = 0;
+ qsizetype cppResult = 0;
auto *converter = Shiboken::Conversions::PrimitiveTypeConverter<qsizetype>();
if (auto *pythonToCpp = Shiboken::Conversions::isPythonToCppConvertible(converter, retVal))
pythonToCpp(retVal, &cppResult);
// PYSIDE-464: The error is only valid before PyErr_Restore,
// PYSIDE-464: therefore we take local copies.
Shiboken::AutoDecRef objStr(PyObject_Str(errValue));
- const QString errString = QLatin1String(Shiboken::String::toCString(objStr));
+ const QString errString = QString::fromUtf8(Shiboken::String::toCString(objStr));
const bool isSyntaxError = errType == PyExc_SyntaxError;
const bool isTypeError = errType == PyExc_TypeError;
PyErr_Restore(errType, errValue, errTraceback);
#include <optional>
-#include <QtCore/QtGlobal>
+#include <QtCore/qtclasshelpermacros.h>
QT_FORWARD_DECLARE_CLASS(QObject)
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "pysideqmlnamedelement_p.h"
-#include "pysideqmltypeinfo_p.h"
#include <pysideclassdecorator_p.h>
#include <pysideqmlregistertype_p.h>
#include <shiboken.h>
#include <signature.h>
-#include <QtCore/QtGlobal>
-
class PySideQmlNamedElementPrivate : public PySide::ClassDecorator::StringDecoratorPrivate
{
public:
return nullptr;
auto *data = DecoratorPrivate::get<PySideQmlNamedElementPrivate>(self);
- auto *result = PySide::Qml::qmlNamedElementMacro(klass, data->string().c_str());
+ auto *result = PySide::Qml::qmlNamedElementMacro(klass, data->string());
Py_XINCREF(result);
return result;
}
#include "pysideqmltypeinfo_p.h"
#include "pysideqmlattached_p.h"
#include "pysideqmlextended_p.h"
+#include "pysideqmluncreatable.h"
#include <limits>
+#include <optional>
// shiboken
#include <shiboken.h>
// pyside
#include <pyside.h>
#include <pysideqobject.h>
+#include <pysideclassinfo.h>
#include <pyside_p.h>
#include <QtCore/QMutex>
#include <QtQml/QJSValue>
#include <QtQml/QQmlListProperty>
#include <private/qqmlmetatype_p.h>
+#include <private/qmetaobjectbuilder_p.h>
+
+#include <memory>
+
+using namespace Qt::StringLiterals;
static PySide::Qml::QuickRegisterItemFunction quickRegisterItemFunction = nullptr;
+static const auto qmlElementKey = "QML.Element"_ba;
+
static void createInto(void *memory, void *type)
{
QMutexLocker locker(&PySide::nextQObjectMemoryAddrMutex());
return inheritsFrom(o, "QPyQmlParserStatus");
}
-namespace PySide::Qml {
+static QByteArray getGlobalString(const char *name)
+{
+ PyObject *globalVar = PyDict_GetItemString(PyEval_GetGlobals(), name);
-int qmlRegisterType(PyObject *pyObj, const char *uri, int versionMajor,
- int versionMinor, const char *qmlName, const char *noCreationReason,
- bool creatable)
+ if (globalVar == nullptr || PyUnicode_Check(globalVar) == 0)
+ return {};
+
+ const char *stringValue = _PepUnicode_AsString(globalVar);
+ return stringValue != nullptr ? QByteArray(stringValue) : QByteArray{};
+}
+
+static int getGlobalInt(const char *name)
{
- using namespace Shiboken;
+ PyObject *globalVar = PyDict_GetItemString(PyEval_GetGlobals(), name);
- PyTypeObject *qobjectType = qObjectType();
+ if (globalVar == nullptr || PyLong_Check(globalVar) == 0)
+ return -1;
- PyTypeObject *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
- if (!PySequence_Contains(pyObjType->tp_mro, reinterpret_cast<PyObject *>(qobjectType))) {
- PyErr_Format(PyExc_TypeError, "A type inherited from %s expected, got %s.",
- qobjectType->tp_name, pyObjType->tp_name);
+ long value = PyLong_AsLong(globalVar);
+
+ if (value > std::numeric_limits<int>::max() || value < std::numeric_limits<int>::min())
return -1;
+
+ return value;
+}
+
+struct ImportData
+{
+ QByteArray importName;
+ int majorVersion = 0;
+ int minorVersion = 0;
+
+ QTypeRevision toTypeRevision() const;
+};
+
+QTypeRevision ImportData::toTypeRevision() const
+{
+ return QTypeRevision::fromVersion(majorVersion, minorVersion);
+}
+
+std::optional<ImportData> getGlobalImportData(const char *decoratorName)
+{
+ ImportData result{getGlobalString("QML_IMPORT_NAME"),
+ getGlobalInt("QML_IMPORT_MAJOR_VERSION"),
+ getGlobalInt("QML_IMPORT_MINOR_VERSION")};
+
+ if (result.importName.isEmpty()) {
+ PyErr_Format(PyExc_TypeError, "You need specify QML_IMPORT_NAME in order to use %s.",
+ decoratorName);
+ return {};
}
- const QMetaObject *metaObject = PySide::retrieveMetaObject(pyObjType);
- Q_ASSERT(metaObject);
+ if (result.majorVersion == -1) {
+ PyErr_Format(PyExc_TypeError, "You need specify QML_IMPORT_MAJOR_VERSION in order to use %s.",
+ decoratorName);
+ return {};
+ }
+
+ // Specifying a minor version is optional
+ if (result.minorVersion == -1)
+ result.minorVersion = 0;
+ return result;
+}
+
+static PyTypeObject *checkTypeObject(PyObject *pyObj, const char *what)
+{
+ if (PyType_Check(pyObj) == 0) {
+ PyErr_Format(PyExc_TypeError, "%s can only be used for classes.", what);
+ return nullptr;
+ }
+ return reinterpret_cast<PyTypeObject *>(pyObj);
+}
+
+static bool setClassInfo(PyTypeObject *type, const QByteArray &key, const QByteArray &value)
+{
+ if (!PySide::ClassInfo::setClassInfo(type, key, value)) {
+ PyErr_Format(PyExc_TypeError, "Setting class info \"%s\" to \"%s\" on \"%s\" failed.",
+ key.constData(), value.constData(), type->tp_name);
+ return false;
+ }
+ return true;
+}
+
+static inline bool setSingletonClassInfo(PyTypeObject *type)
+{
+ return setClassInfo(type, "QML.Singleton"_ba, "true"_ba);
+}
+
+static QQmlCustomParser *defaultCustomParserFactory()
+{
+ return nullptr;
+}
+
+namespace PySide::Qml {
+
+// Modern (6.7) type registration using RegisterTypeAndRevisions
+// and information set to QMetaClassInfo.
+static int qmlRegisterType(PyObject *pyObj,
+ const ImportData &importData,
+ const QMetaObject *metaObject,
+ const QMetaObject *classInfoMetaObject = nullptr)
+{
+ PyTypeObject *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
+
+ if (classInfoMetaObject == nullptr)
+ classInfoMetaObject = metaObject;
// Register as simple QObject rather than Qt Quick item.
// Incref the type object, don't worry about decref'ing it because
const auto attachedInfo = qmlAttachedInfo(pyObjType, typeInfo);
const auto extendedInfo = qmlExtendedInfo(pyObj, typeInfo);
- QQmlPrivate::RegisterType type {
+ QList<int> ids;
+ QQmlPrivate::RegisterTypeAndRevisions type {
QQmlPrivate::RegisterType::StructVersion::Base, // structVersion
typeId, listId, objectSize,
- creatable ? createInto : nullptr, // create
+ createInto, // create
pyObj, // userdata
- QString::fromUtf8(noCreationReason),
nullptr, // createValueType (Remove in Qt 7)
- uri,
- QTypeRevision::fromVersion(versionMajor, versionMinor), // version
- qmlName, // elementName
+ importData.importName.constData(),
+ importData.toTypeRevision(), // version
metaObject,
+ classInfoMetaObject,
attachedInfo.factory, // attachedPropertiesFunction
attachedInfo.metaObject, // attachedPropertiesMetaObject
0, 0, 0, // parserStatusCast, valueSourceCast, valueInterceptorCast
extendedInfo.factory, // extensionObjectCreate
extendedInfo.metaObject, // extensionMetaObject
- nullptr, // customParser
- {}, // revision
+ defaultCustomParserFactory, // customParser
+ &ids, // qmlTypeIds
0, // finalizerCast
- QQmlPrivate::ValueTypeCreationMethod::None // creationMethod
+ false, // forceAnonymous
+ {} // listMetaSequence
};
// Allow registering Qt Quick items.
QQmlPrivate::StaticCastSelector<QObject, QQmlPropertyValueInterceptor>::cast();
}
- int qmlTypeId = QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
+ QQmlPrivate::qmlregister(QQmlPrivate::TypeAndRevisionsRegistration, &type);
+ const int qmlTypeId = ids.value(0, -1);
if (qmlTypeId == -1) {
PyErr_Format(PyExc_TypeError, "QML meta type registration of \"%s\" failed.",
- qmlName);
+ typeName.constData());
}
return qmlTypeId;
}
-int qmlRegisterSingletonType(PyObject *pyObj, const char *uri, int versionMajor,
- int versionMinor, const char *qmlName, PyObject *callback,
- bool isQObject, bool hasCallback)
+static int qmlRegisterType(PyObject *pyObj, PyObject *pyClassInfoObj,
+ const ImportData &importData)
{
- using namespace Shiboken;
+ PyTypeObject *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
+ if (!isQObjectDerived(pyObjType, true))
+ return -1;
- if (hasCallback) {
- if (!PyCallable_Check(callback)) {
- PyErr_Format(PyExc_TypeError, "Invalid callback specified.");
- return -1;
- }
+ const QMetaObject *metaObject = PySide::retrieveMetaObject(pyObjType);
+ Q_ASSERT(metaObject);
+ const QMetaObject *classInfoMetaObject = pyObj == pyClassInfoObj
+ ? metaObject : PySide::retrieveMetaObject(pyClassInfoObj);
+ return qmlRegisterType(pyObj, importData, metaObject, classInfoMetaObject);
+}
- AutoDecRef funcCode(PyObject_GetAttrString(callback, "__code__"));
- AutoDecRef argCount(PyObject_GetAttrString(funcCode, "co_argcount"));
+// Legacy (pre 6.7) compatibility helper for the free register functions.
+int qmlRegisterType(PyObject *pyObj, const char *uri, int versionMajor, int versionMinor,
+ const char *qmlName, const char *noCreationReason,
+ bool creatable)
+{
+ auto *type = checkTypeObject(pyObj, "qmlRegisterType()");
+ if (type == nullptr || !PySide::isQObjectDerived(type, true))
+ return -1;
- int count = PyLong_AsLong(argCount);
+ const QMetaObject *metaObject = PySide::retrieveMetaObject(type);
+ Q_ASSERT(metaObject);
- if (count != 1) {
- PyErr_Format(PyExc_TypeError, "Callback has a bad parameter count.");
- return -1;
+ // PYSIDE-2709: Use a separate QMetaObject for the class information
+ // as modifying metaObject breaks inheritance.
+ QMetaObjectBuilder classInfobuilder(&QObject::staticMetaObject);
+ classInfobuilder.addClassInfo(qmlElementKey, qmlName);
+ if (!creatable)
+ setUncreatableClassInfo(&classInfobuilder, noCreationReason);
+ auto *classInfoMetaObject = classInfobuilder.toMetaObject();
+
+ const int qmlTypeId = qmlRegisterType(pyObj, {uri, versionMajor, versionMinor},
+ metaObject, classInfoMetaObject);
+ free(classInfoMetaObject);
+ return qmlTypeId;
+}
+
+// Singleton helpers
+
+// Check the arguments of a singleton callback (C++: "QJSValue cb(QQmlEngine *, QJSEngine *)",
+// but we drop the QJSEngine since it will be the same as QQmlEngine when the latter exists.
+static bool checkSingletonCallback(PyObject *callback)
+{
+ if (callback == nullptr) {
+ PyErr_SetString(PyExc_TypeError, "No callback specified.");
+ return false;
+ }
+ if (PyCallable_Check(callback) == 0) {
+ PyErr_Format(PyExc_TypeError, "Invalid callback specified (%S).", callback);
+ return false;
+ }
+ Shiboken::AutoDecRef funcCode(PyObject_GetAttrString(callback, "__code__"));
+ if (funcCode.isNull()) {
+ PyErr_Format(PyExc_TypeError, "Cannot retrieve code of callback (%S).", callback);
+ return false;
+ }
+ Shiboken::AutoDecRef argCountAttr(PyObject_GetAttrString(funcCode, "co_argcount"));
+ const int argCount = PyLong_AsLong(argCountAttr.object());
+ if (argCount != 1) {
+ PyErr_Format(PyExc_TypeError, "Callback (%S) has %d parameter(s), expected one.",
+ callback, argCount);
+ return false;
+ }
+
+ return true;
+}
+
+// Shared data of a singleton creation callback which dereferences an object on
+// destruction.
+class SingletonQObjectCreationSharedData
+{
+public:
+ Q_DISABLE_COPY_MOVE(SingletonQObjectCreationSharedData)
+
+ SingletonQObjectCreationSharedData(PyObject *cb, PyObject *ref = nullptr) noexcept :
+ callable(cb), reference(ref)
+ {
+ Py_XINCREF(ref);
+ }
+
+ // FIXME: Currently, the QML registration data are in global static variables
+ // and thus cleaned up after Python terminates. Once they are cleaned up
+ // by the QML engine, the code can be activated for proper cleanup of the references.
+ ~SingletonQObjectCreationSharedData()
+#if 0 //
+ ~SingletonQObjectCreationSharedData()
+ {
+ if (reference != nullptr) {
+ Shiboken::GilState gil;
+ Py_DECREF(reference);
}
+ }
+#else
+ = default;
+#endif
+
+ PyObject *callable{}; // Callback, static method or type object to be invoked.
+ PyObject *reference{}; // Object to dereference when going out scope
+};
+
+// Base class for QML singleton creation callbacks with helper for error checking.
+class SingletonQObjectCreationBase
+{
+protected:
+ explicit SingletonQObjectCreationBase(PyObject *cb, PyObject *ref = nullptr) :
+ m_data(std::make_shared<SingletonQObjectCreationSharedData>(cb, ref))
+ {
+ }
+
+ static QObject *handleReturnValue(PyObject *retVal);
+
+ std::shared_ptr<SingletonQObjectCreationSharedData> data() const { return m_data; }
+
+private:
+ std::shared_ptr<SingletonQObjectCreationSharedData> m_data;
+};
+
+QObject *SingletonQObjectCreationBase::handleReturnValue(PyObject *retVal)
+{
+ using Shiboken::Conversions::isPythonToCppPointerConvertible;
+ // Make sure the callback returns something we can convert, else the entire application will crash.
+ if (retVal == nullptr) {
+ PyErr_Format(PyExc_TypeError, "Callback returns 0 value.");
+ return nullptr;
+ }
+ if (isPythonToCppPointerConvertible(qObjectType(), retVal) == nullptr) {
+ PyErr_Format(PyExc_TypeError, "Callback returns invalid value (%S).", retVal);
+ return nullptr;
+ }
+
+ QObject *obj = nullptr;
+ Shiboken::Conversions::pythonToCppPointer(qObjectType(), retVal, &obj);
+ return obj;
+}
+
+// QML singleton creation callback by invoking a type object
+class SingletonQObjectFromTypeCreation : public SingletonQObjectCreationBase
+{
+public:
+ explicit SingletonQObjectFromTypeCreation(PyObject *typeObj) :
+ SingletonQObjectCreationBase(typeObj, typeObj) {}
+
+ QObject *operator ()(QQmlEngine *, QJSEngine *) const
+ {
+ Shiboken::GilState gil;
+ Shiboken::AutoDecRef args(PyTuple_New(0));
+ PyObject *retVal = PyObject_CallObject(data()->callable, args);
+ QObject *result = handleReturnValue(retVal);
+ if (result == nullptr)
+ Py_XDECREF(retVal);
+ return result;
+ }
+};
+
+// QML singleton creation by invoking a callback, passing QQmlEngine. Keeps a
+// references to the the callback.
+class SingletonQObjectCallbackCreation : public SingletonQObjectCreationBase
+{
+public:
+ explicit SingletonQObjectCallbackCreation(PyObject *callback) :
+ SingletonQObjectCreationBase(callback, callback) {}
+ explicit SingletonQObjectCallbackCreation(PyObject *callback, PyObject *ref) :
+ SingletonQObjectCreationBase(callback, ref) {}
+
+ QObject *operator ()(QQmlEngine *engine, QJSEngine *) const
+ {
+ Shiboken::GilState gil;
+ Shiboken::AutoDecRef args(PyTuple_New(1));
+ PyTuple_SET_ITEM(args, 0,
+ Shiboken::Conversions::pointerToPython(qQmlEngineType(), engine));
+ PyObject *retVal = PyObject_CallObject(data()->callable, args);
+ QObject *result = handleReturnValue(retVal);
+ if (result == nullptr)
+ Py_XDECREF(retVal);
+ return result;
+ }
+};
+
+using SingletonQObjectCreation = std::function<QObject*(QQmlEngine *, QJSEngine *)>;
+
+// Modern (6.7) singleton type registration using RegisterSingletonTypeAndRevisions
+// and information set to QMetaClassInfo (QObject only pending QTBUG-110467).
+static int qmlRegisterSingletonTypeV2(PyObject *pyObj, PyObject *pyClassInfoObj,
+ const ImportData &importData,
+ const SingletonQObjectCreation &callback)
+{
+ PyTypeObject *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
+ if (!isQObjectDerived(pyObjType, true))
+ return -1;
- // Make sure the callback never gets deallocated
- Py_INCREF(callback);
+ const QMetaObject *metaObject = PySide::retrieveMetaObject(pyObjType);
+ Q_ASSERT(metaObject);
+ const QMetaObject *classInfoMetaObject = pyObj == pyClassInfoObj
+ ? metaObject : PySide::retrieveMetaObject(pyClassInfoObj);
+
+ QList<int> ids;
+ QQmlPrivate::RegisterSingletonTypeAndRevisions type {
+ QQmlPrivate::RegisterType::StructVersion::Base, // structVersion
+ importData.importName.constData(),
+ importData.toTypeRevision(), // version
+ callback, // qObjectApi,
+ metaObject,
+ classInfoMetaObject,
+ QMetaType(QMetaType::QObjectStar), // typeId
+ nullptr, // extensionMetaObject
+ nullptr, // extensionObjectCreate
+ &ids
+ };
+
+ QQmlPrivate::qmlregister(QQmlPrivate::SingletonAndRevisionsRegistration, &type);
+ const int qmlTypeId = ids.value(0, -1);
+ if (qmlTypeId == -1) {
+ PyErr_Format(PyExc_TypeError, "Singleton QML meta type registration of \"%s\" failed.",
+ pyObjType->tp_name);
}
+ return qmlTypeId;
+}
+
+// Legacy (pre 6.7) singleton type registration using RegisterSingletonType
+// for QObject and value types. Still used by qmlRegisterSingletonType()
+// for the hypothetical case of a value type.
+static int qmlRegisterSingletonType(PyObject *pyObj, const ImportData &importData,
+ const char *qmlName, PyObject *callback,
+ bool isQObject, bool hasCallback)
+{
+ if (hasCallback && !checkSingletonCallback(callback))
+ return -1;
const QMetaObject *metaObject = nullptr;
if (!isQObjectDerived(pyObjType, true))
return -1;
- // If we don't have a callback we'll need the pyObj to stay allocated indefinitely
- if (!hasCallback)
- Py_INCREF(pyObj);
-
metaObject = PySide::retrieveMetaObject(pyObjType);
Q_ASSERT(metaObject);
}
QQmlPrivate::RegisterSingletonType type {
QQmlPrivate::RegisterType::StructVersion::Base, // structVersion
- uri,
- QTypeRevision::fromVersion(versionMajor, versionMinor), // version
+ importData.importName.constData(),
+ importData.toTypeRevision(), // version
qmlName, // typeName
{}, // scriptApi
{}, // qObjectApi
// FIXME: Fix this to assign new type ids each time.
type.typeId = QMetaType(QMetaType::QObjectStar);
- type.qObjectApi =
- [callback, pyObj, hasCallback](QQmlEngine *engine, QJSEngine *) -> QObject * {
- Shiboken::GilState gil;
- AutoDecRef args(PyTuple_New(hasCallback ? 1 : 0));
-
- if (hasCallback) {
- PyTuple_SET_ITEM(args, 0, Conversions::pointerToPython(
- qQmlEngineType(), engine));
- }
-
- AutoDecRef retVal(PyObject_CallObject(hasCallback ? callback : pyObj, args));
-
- // Make sure the callback returns something we can convert, else the entire application will crash.
- if (retVal.isNull() ||
- Conversions::isPythonToCppPointerConvertible(qObjectType(), retVal) == nullptr) {
- PyErr_Format(PyExc_TypeError, "Callback returns invalid value.");
- return nullptr;
- }
-
- QObject *obj = nullptr;
- Conversions::pythonToCppPointer(qObjectType(), retVal, &obj);
-
- if (obj != nullptr)
- Py_INCREF(retVal);
-
- return obj;
- };
+ if (hasCallback)
+ type.qObjectApi = SingletonQObjectCallbackCreation(callback);
+ else
+ type.qObjectApi = SingletonQObjectFromTypeCreation(pyObj);
} else {
type.scriptApi =
[callback](QQmlEngine *engine, QJSEngine *) -> QJSValue {
+ using namespace Shiboken;
+
Shiboken::GilState gil;
AutoDecRef args(PyTuple_New(1));
return QQmlPrivate::qmlregister(QQmlPrivate::SingletonRegistration, &type);
}
-int qmlRegisterSingletonInstance(PyObject *pyObj, const char *uri, int versionMajor,
- int versionMinor, const char *qmlName,
- PyObject *instanceObject)
+// Legacy (pre 6.7) compatibility helper for the free register functions.
+int qmlRegisterSingletonType(PyObject *pyObj,const char *uri,
+ int versionMajor, int versionMinor, const char *qmlName,
+ PyObject *callback, bool isQObject, bool hasCallback)
+{
+ return qmlRegisterSingletonType(pyObj, {uri, versionMajor, versionMinor}, qmlName,
+ callback, isQObject, hasCallback);
+}
+
+// Modern (6.7) singleton instance registration using RegisterSingletonTypeAndRevisions
+// and information set to QMetaClassInfo (QObject only).
+static int qmlRegisterSingletonInstance(PyObject *pyObj, const ImportData &importData,
+ PyObject *instanceObject)
{
using namespace Shiboken;
const QMetaObject *metaObject = PySide::retrieveMetaObject(pyObjType);
Q_ASSERT(metaObject);
- // FIXME: Fix this to assign new type ids each time.
- const QMetaType typeId = QMetaType(QMetaType::QObjectStar);
-
- QQmlPrivate::RegisterSingletonType type {
+ QList<int> ids;
+ QQmlPrivate::RegisterSingletonTypeAndRevisions type {
QQmlPrivate::RegisterType::StructVersion::Base, // structVersion
- uri,
- QTypeRevision::fromVersion(versionMajor, versionMinor), // version
- qmlName, // typeName
- {}, // scriptApi
- registrationFunctor, // qObjectApi
- metaObject, // instanceMetaObject
- typeId,
+ importData.importName.constData(),
+ importData.toTypeRevision(), // version
+ registrationFunctor, // qObjectApi,
+ metaObject,
+ metaObject, // classInfoMetaObject
+ QMetaType(QMetaType::QObjectStar), // typeId
nullptr, // extensionMetaObject
nullptr, // extensionObjectCreate
- {} // revision
+ &ids
};
- return QQmlPrivate::qmlregister(QQmlPrivate::SingletonRegistration, &type);
-}
-
-} // namespace PySide::Qml
-
-static std::string getGlobalString(const char *name)
-{
- using Shiboken::AutoDecRef;
-
- PyObject *globals = PyEval_GetGlobals();
-
- AutoDecRef pyName(Py_BuildValue("s", name));
-
- PyObject *globalVar = PyDict_GetItem(globals, pyName);
-
- if (globalVar == nullptr || !PyUnicode_Check(globalVar))
- return "";
-
- const char *stringValue = _PepUnicode_AsString(globalVar);
- return stringValue != nullptr ? stringValue : "";
+ QQmlPrivate::qmlregister(QQmlPrivate::SingletonAndRevisionsRegistration, &type);
+ return ids.value(0, -1);
}
-static int getGlobalInt(const char *name)
+// Legacy (pre 6.7) compatibility helper for the free register functions.
+int qmlRegisterSingletonInstance(PyObject *pyObj, const char *uri, int versionMajor,
+ int versionMinor, const char *qmlName,
+ PyObject *instanceObject)
{
- using Shiboken::AutoDecRef;
-
- PyObject *globals = PyEval_GetGlobals();
-
- AutoDecRef pyName(Py_BuildValue("s", name));
-
- PyObject *globalVar = PyDict_GetItem(globals, pyName);
-
- if (globalVar == nullptr || !PyLong_Check(globalVar))
+ auto *type = checkTypeObject(pyObj, "qmlRegisterSingletonInstance()");
+ if (type == nullptr || !setClassInfo(type, qmlElementKey, qmlName)
+ || !setSingletonClassInfo(type)) {
return -1;
-
- long value = PyLong_AsLong(globalVar);
-
- if (value > std::numeric_limits<int>::max() || value < std::numeric_limits<int>::min())
- return -1;
-
- return value;
+ }
+ return qmlRegisterSingletonInstance(pyObj, {uri, versionMajor, versionMinor},
+ instanceObject);
}
+} // namespace PySide::Qml
+
enum class RegisterMode {
Normal,
- Anonymous,
- Uncreatable,
Singleton
};
-static PyObject *qmlElementMacroHelper(PyObject *pyObj,
- const char *decoratorName,
- const char *typeName = nullptr,
- RegisterMode mode = RegisterMode::Normal,
- const char *noCreationReason = nullptr)
+namespace PySide::Qml {
+
+// Check for a static create() method on a decorated singleton.
+// Might set a Python error if the check fails.
+static std::optional<SingletonQObjectCreation>
+ singletonCreateMethod(PyTypeObject *pyObjType)
{
- if (!PyType_Check(pyObj)) {
- PyErr_Format(PyExc_TypeError, "This decorator can only be used on classes.");
- return nullptr;
+ Shiboken::AutoDecRef tpDict(PepType_GetDict(pyObjType));
+ auto *create = PyDict_GetItemString(tpDict.object(), "create");
+ // Method decorated by "@staticmethod"
+ if (create == nullptr || std::strcmp(Py_TYPE(create)->tp_name, "staticmethod") != 0)
+ return std::nullopt;
+ // 3.10: "__wrapped__"
+ Shiboken::AutoDecRef function(PyObject_GetAttrString(create, "__func__"));
+ if (function.isNull()) {
+ PyErr_Format(PyExc_TypeError, "Cannot retrieve function of callback (%S).",
+ create);
+ return std::nullopt;
}
+ if (!checkSingletonCallback(function.object()))
+ return std::nullopt;
+ // Reference to the type needs to be kept.
+ return SingletonQObjectCallbackCreation(function.object(),
+ reinterpret_cast<PyObject *>(pyObjType));
+}
- PyTypeObject *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
- if (typeName == nullptr)
- typeName = pyObjType->tp_name;
- if (!PySequence_Contains(pyObjType->tp_mro, reinterpret_cast<PyObject *>(qObjectType()))) {
- PyErr_Format(PyExc_TypeError, "This decorator can only be used with classes inherited from QObject, got %s.",
- typeName);
+PyObject *qmlElementMacro(PyObject *pyObj, const char *decoratorName,
+ const QByteArray &typeName)
+{
+ auto *pyObjType = checkTypeObject(pyObj, decoratorName);
+ if (pyObjType == nullptr)
return nullptr;
- }
-
- std::string importName = getGlobalString("QML_IMPORT_NAME");
- int majorVersion = getGlobalInt("QML_IMPORT_MAJOR_VERSION");
- int minorVersion = getGlobalInt("QML_IMPORT_MINOR_VERSION");
- if (importName.empty()) {
- PyErr_Format(PyExc_TypeError, "You need specify QML_IMPORT_NAME in order to use %s.",
- decoratorName);
+ if (!PySide::isQObjectDerived(pyObjType, false)) {
+ PyErr_Format(PyExc_TypeError,
+ "%s can only be used with classes inherited from QObject, got %s.",
+ decoratorName, pyObjType->tp_name);
return nullptr;
}
- if (majorVersion == -1) {
- PyErr_Format(PyExc_TypeError, "You need specify QML_IMPORT_MAJOR_VERSION in order to use %s.",
- decoratorName);
- return nullptr;
- }
-
- // Specifying a minor version is optional
- if (minorVersion == -1)
- minorVersion = 0;
-
- const char *uri = importName.c_str();
- const int result = mode == RegisterMode::Singleton
- ? PySide::Qml::qmlRegisterSingletonType(pyObj, uri, majorVersion, minorVersion,
- typeName, nullptr,
- PySide::isQObjectDerived(pyObjType, false),
- false)
- : PySide::Qml::qmlRegisterType(pyObj, uri, majorVersion, minorVersion,
- mode != RegisterMode::Anonymous ? typeName : nullptr,
- noCreationReason,
- mode == RegisterMode::Normal);
-
- if (result == -1) {
- PyErr_Format(PyExc_TypeError, "%s: Failed to register type %s.",
- decoratorName, typeName);
- }
-
- return pyObj;
-}
-
-namespace PySide::Qml {
+ if (!setClassInfo(pyObjType, qmlElementKey, typeName))
+ return nullptr;
-PyObject *qmlElementMacro(PyObject *pyObj, const char *decoratorName,
- const char *typeName = nullptr)
-{
RegisterMode mode = RegisterMode::Normal;
- const char *noCreationReason = nullptr;
const auto info = PySide::Qml::qmlTypeInfo(pyObj);
auto *registerObject = pyObj;
if (info) {
- if (info->flags.testFlag(PySide::Qml::QmlTypeFlag::Singleton))
+ if (info->flags.testFlag(PySide::Qml::QmlTypeFlag::Singleton)) {
mode = RegisterMode::Singleton;
- else if (info->flags.testFlag(PySide::Qml::QmlTypeFlag::Uncreatable))
- mode = RegisterMode::Uncreatable;
- noCreationReason = info->noCreationReason.c_str();
+ setSingletonClassInfo(pyObjType);
+ }
if (info->foreignType)
registerObject = reinterpret_cast<PyObject *>(info->foreignType);
}
- if (!qmlElementMacroHelper(registerObject, decoratorName, typeName, mode, noCreationReason))
+
+ const auto importDataO = getGlobalImportData(decoratorName);
+ if (!importDataO.has_value())
+ return nullptr;
+ const auto importData = importDataO.value();
+
+ int result{};
+ if (mode == RegisterMode::Singleton) {
+ auto singletonCreateMethodO = singletonCreateMethod(pyObjType);
+ if (!singletonCreateMethodO.has_value()) {
+ if (PyErr_Occurred() != nullptr)
+ return nullptr;
+ singletonCreateMethodO = SingletonQObjectFromTypeCreation(pyObj);
+ }
+ result = PySide::Qml::qmlRegisterSingletonTypeV2(registerObject, pyObj, importData,
+ singletonCreateMethodO.value());
+ } else {
+ result = PySide::Qml::qmlRegisterType(registerObject, pyObj, importData);
+ }
+ if (result == -1) {
+ PyErr_Format(PyExc_TypeError, "%s: Failed to register type %s.",
+ decoratorName, pyObjType->tp_name);
return nullptr;
+ }
+
return pyObj;
}
PyObject *qmlElementMacro(PyObject *pyObj)
{
- return qmlElementMacro(pyObj, "QmlElement");
+ return qmlElementMacro(pyObj, "QmlElement", "auto"_ba);
}
-PyObject *qmlNamedElementMacro(PyObject *pyObj, const char *typeName)
+PyObject *qmlNamedElementMacro(PyObject *pyObj, const QByteArray &typeName)
{
- return qmlElementMacro(pyObj, "QmlNamedElement", qstrdup(typeName));
+ return qmlElementMacro(pyObj, "QmlNamedElement", typeName);
}
PyObject *qmlAnonymousMacro(PyObject *pyObj)
{
- return qmlElementMacroHelper(pyObj, "QmlAnonymous", nullptr,
- RegisterMode::Anonymous);
+ return qmlElementMacro(pyObj, "QmlAnonymous", "anonymous"_ba);
}
PyObject *qmlSingletonMacro(PyObject *pyObj)
QT_BEGIN_NAMESPACE
namespace QQmlPrivate
{
-struct RegisterType;
+struct RegisterTypeAndRevisions;
}
QT_END_NAMESPACE
/**
* PySide implementation of qmlRegisterType<T> function.
*
+ * This is a helper for the legacy free qmlRegisterType*() type functions.
+ * Decorators should be used instead.
+ *
* \param pyObj Python type to be registered.
* \param uri QML element uri.
* \param versionMajor QML component major version.
/**
* PySide implementation of qmlRegisterSingletonType<T> function.
*
+ * This is a helper for the legacy free qmlRegisterSingletonType<T> type function.
+ * Decorators should be used instead.
+ *
* \param pyObj Python type to be registered.
* \param uri QML element uri.
* \param versionMajor QML component major version.
// Used by QtQuick module to fill the QQmlPrivate::RegisterType::parserStatusCast,
// valueSourceCast and valueInterceptorCast fields with the correct values.
using QuickRegisterItemFunction =
- bool (*)(PyObject *pyObj, QT_PREPEND_NAMESPACE(QQmlPrivate::RegisterType) *);
+ bool (*)(PyObject *pyObj, QT_PREPEND_NAMESPACE(QQmlPrivate::RegisterTypeAndRevisions) *);
PYSIDEQML_API QuickRegisterItemFunction getQuickRegisterItemFunction();
PYSIDEQML_API void setQuickRegisterItemFunction(QuickRegisterItemFunction function);
#include <sbkpython.h>
+#include <QtCore/QByteArray>
+
PyTypeObject *qObjectType();
namespace PySide::Qml {
-PyObject *qmlNamedElementMacro(PyObject *pyObj, const char *typeName);
+PyObject *qmlNamedElementMacro(PyObject *pyObj, const QByteArray &typeName);
}
d.noquote();
d.nospace();
d << "QmlTypeInfo(" << i.flags;
- if (!i.noCreationReason.empty())
- d << ", noCreationReason=\"" << i.noCreationReason.c_str() << '"';
if (i.foreignType)
d << ", foreignType=" << i.foreignType->tp_name;
if (i.attachedType)
#include <sbkpython.h>
+#include <QtCore/QByteArray>
#include <QtCore/QFlags>
#include <memory>
-#include <string>
QT_FORWARD_DECLARE_CLASS(QDebug)
QT_FORWARD_DECLARE_CLASS(QObject)
enum class QmlTypeFlag
{
- Singleton = 0x1,
- Uncreatable = 0x2
+ Singleton = 0x1
};
Q_DECLARE_FLAGS(QmlTypeFlags, QmlTypeFlag)
struct QmlTypeInfo
{
QmlTypeFlags flags;
- std::string noCreationReason;
PyTypeObject *foreignType = nullptr;
PyTypeObject *attachedType = nullptr;
PyTypeObject *extensionType = nullptr;
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "pysideqmluncreatable.h"
-#include "pysideqmltypeinfo_p.h"
#include <pysideclassdecorator_p.h>
+#include <pysideclassinfo.h>
#include <shiboken.h>
#include <signature.h>
#include <sbkcppstring.h>
-#include <string>
-#include <unordered_map>
+#include <QtCore/qbytearray.h>
+#include <private/qmetaobjectbuilder_p.h>
-#include <QtCore/QtGlobal>
+using namespace Qt::StringLiterals;
class PySideQmlUncreatablePrivate : public PySide::ClassDecorator::StringDecoratorPrivate
{
if (klass== nullptr)
return nullptr;
+ auto *type = reinterpret_cast<PyTypeObject *>(klass);
auto *data = DecoratorPrivate::get<PySideQmlUncreatablePrivate>(self);
-
- const auto info = PySide::Qml::ensureQmlTypeInfo(klass);
- info->flags.setFlag(PySide::Qml::QmlTypeFlag::Uncreatable);
- info->noCreationReason = data->string();
+ setUncreatableClassInfo(type, data->string());
Py_INCREF(klass);
return klass;
PyModule_AddObject(module, "QmlUncreatable",
reinterpret_cast<PyObject *>(PySideQmlUncreatable_TypeF()));
}
+
+void setUncreatableClassInfo(PyTypeObject *type, const QByteArray &reason)
+{
+ PySide::ClassInfo::setClassInfo(type, {
+ {"QML.Creatable"_ba, "false"_ba},
+ {"QML.UncreatableReason"_ba, reason} });
+}
+
+void setUncreatableClassInfo(QMetaObjectBuilder *builder, const QByteArray &reason)
+{
+ builder->addClassInfo("QML.Creatable", "false");
+ builder->addClassInfo("QML.UncreatableReason", reason);
+}
#include <sbkpython.h>
+#include <QtCore/QByteArray>
+
+QT_FORWARD_DECLARE_CLASS(QMetaObjectBuilder)
+
// The QmlUncreatable decorator modifies QmlElement to register an uncreatable
// type. Due to the (reverse) execution order of decorators, it needs to follow
// QmlElement.
void initQmlUncreatable(PyObject *module);
+void setUncreatableClassInfo(PyTypeObject *type, const QByteArray &reason);
+void setUncreatableClassInfo(QMetaObjectBuilder *builder, const QByteArray &reason);
+
#endif // PYSIDEQMLUNCREATABLE_H
)
# See libshiboken/CMakeLists.txt
+
+target_compile_definitions(PySidePlugin PRIVATE -DQT_NO_KEYWORDS=1)
+
if(PYTHON_LIMITED_API)
target_compile_definitions(PySidePlugin PRIVATE "-DPy_LIMITED_API=0x03050000")
endif()
static QString pyErrorMessage()
{
- QString result = QLatin1String("<error information not available>");
+ QString result = "<error information not available>"_L1;
PyObject *ptype = {};
PyObject *pvalue = {};
PyObject *ptraceback = {};
{
PyObject *main = PyImport_AddModule("__main__");
if (main == nullptr) {
- *errorMessage = QLatin1String("Internal error: Cannot retrieve __main__");
+ *errorMessage = "Internal error: Cannot retrieve __main__"_L1;
return false;
}
PyObject *globalDictionary = PyModule_GetDict(main);
file.close();
const bool ok = runPyScript(script.constData(), errorMessage);
if (!ok && !errorMessage->isEmpty()) {
- errorMessage->prepend(QLatin1String("Error running ") + fileName
- + QLatin1String(": "));
+ errorMessage->prepend("Error running "_L1 + fileName + ": "_L1);
}
return ok;
}
#include <QtUiPlugin/QDesignerCustomWidgetInterface>
-#include <QtCore/qglobal.h>
-
class PyCustomWidget: public QObject, public QDesignerCustomWidgetInterface
{
Q_OBJECT
# the path to the testbinding module
get_filename_component(BUILD_DIR "${CMAKE_BINARY_DIR}" DIRECTORY)
get_filename_component(BUILD_DIR "${CMAKE_BINARY_DIR}" DIRECTORY)
-set(QT_DIR "${_qt5Core_install_prefix}")
+set(QT_DIR "${QT6_INSTALL_PREFIX}")
macro(TEST_QT_MODULE var name)
if(NOT DISABLE_${name} AND ${var})
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+'''Test cases for QtAsyncio'''
+
+import asyncio
+import unittest
+
+import PySide6.QtAsyncio as QtAsyncio
+
+
+class QAsyncioTestCaseCancelTaskGroup(unittest.TestCase):
+ def setUp(self) -> None:
+ super().setUp()
+ # We only reach the end of the loop if the task is not cancelled.
+ self.loop_end_reached = False
+
+ async def raise_error(self):
+ raise RuntimeError
+
+ async def loop_short(self):
+ self._loop_end_reached = False
+ for _ in range(1000):
+ await asyncio.sleep(1e-3)
+ self._loop_end_reached = True
+
+ async def loop_shorter(self):
+ self._loop_end_reached = False
+ for _ in range(1000):
+ await asyncio.sleep(1e-4)
+ self._loop_end_reached = True
+
+ async def loop_the_shortest(self):
+ self._loop_end_reached = False
+ for _ in range(1000):
+ await asyncio.to_thread(lambda: None)
+ self._loop_end_reached = True
+
+ async def main(self, coro):
+ async with asyncio.TaskGroup() as tg:
+ tg.create_task(coro())
+ tg.create_task(self.raise_error())
+
+ def test_cancel_taskgroup(self):
+ coros = [self.loop_short, self.loop_shorter, self.loop_the_shortest]
+
+ for coro in coros:
+ try:
+ QtAsyncio.run(self.main(coro), keep_running=False)
+ except ExceptionGroup as e:
+ self.assertEqual(len(e.exceptions), 1)
+ self.assertIsInstance(e.exceptions[0], RuntimeError)
+ self.assertFalse(self._loop_end_reached)
+
+
+if __name__ == '__main__':
+ unittest.main()
PYSIDE_TEST(bug_PYSIDE-41.py)
PYSIDE_TEST(bug_PYSIDE-42.py)
PYSIDE_TEST(bug_PYSIDE-164.py)
+PYSIDE_TEST(bug_PYSIDE-2745.py)
PYSIDE_TEST(blocking_signals_test.py)
PYSIDE_TEST(classinfo_test.py)
PYSIDE_TEST(child_event_test.py)
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, SIGNAL, QFile, QSignalBlocker
+from PySide6.QtCore import QObject, Signal, QFile, QSignalBlocker
+
+
+class Sender(QObject):
+ mysignal = Signal()
+ mysignal_int_int = Signal(int, int)
class TestSignalsBlockedBasic(unittest.TestCase):
def setUp(self):
# Set up the basic resources needed
- self.obj = QObject()
+ self.obj = Sender()
self.args = tuple()
self.called = False
def testShortCircuitSignals(self):
# Blocking of Python short-circuit signals
- QObject.connect(self.obj, SIGNAL('mysignal()'), self.callback)
+ self.obj.mysignal.connect(self.callback)
- self.obj.emit(SIGNAL('mysignal()'))
+ self.obj.mysignal.emit()
self.assertTrue(self.called)
self.called = False
self.obj.blockSignals(True)
- self.obj.emit(SIGNAL('mysignal()'))
+ self.obj.mysignal.emit()
self.assertTrue(not self.called)
def testPythonSignals(self):
# Blocking of Python typed signals
- QObject.connect(self.obj, SIGNAL('mysignal(int,int)'), self.callback)
+
+ self.obj.mysignal_int_int.connect(self.callback)
self.args = (1, 3)
- self.obj.emit(SIGNAL('mysignal(int,int)'), *self.args)
+ self.obj.mysignal_int_int.emit(*self.args)
self.assertTrue(self.called)
self.called = False
self.obj.blockSignals(True)
- self.obj.emit(SIGNAL('mysignal(int,int)'), *self.args)
+ self.obj.mysignal_int_int.emit(*self.args)
self.assertTrue(not self.called)
def testAboutToCloseBlocking(self):
# QIODevice.aboutToClose() blocking
- QObject.connect(self.qfile, SIGNAL('aboutToClose()'), self.callback)
+ self.qfile.aboutToClose.connect(self.callback)
self.assertTrue(self.qfile.open(QFile.ReadOnly))
self.qfile.close()
class Task(QRunnable):
def run(self):
- QThread.sleep(2) # Sleep 2 seconds
+ QThread.msleep(100)
class QThreadPoolTest(unittest.TestCase):
for i in range(3):
task = Task()
QThreadPool.globalInstance().start(task)
- time.sleep(1) # Sleep 1 second
+ time.sleep(0.05)
- QThreadPool.globalInstance().waitForDone()
+ self.assertTrue(QThreadPool.globalInstance().waitForDone())
def testCallable(self):
global thread_function_called
tp = QThreadPool.globalInstance()
tp.start(thread_function)
- tp.waitForDone()
+ self.assertTrue(tp.waitForDone())
self.assertTrue(thread_function_called)
def testInvalidDisconnection(self):
o = QObject()
- self.assertRaises(RuntimeError, o.destroyed.disconnect, self.callback)
+ self.assertFalse(o.destroyed.disconnect(self.callback))
if __name__ == '__main__':
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QCoreApplication, QEventLoop, QObject, Qt, QThread, QTimer, SIGNAL
+from PySide6.QtCore import QCoreApplication, QEventLoop, QObject, Qt, QThread, Signal
class Emitter(QThread):
+
+ signal = Signal(int)
+
def __init__(self):
super().__init__()
def run(self):
print("Before emit.")
- self.emit(SIGNAL("signal(int)"), 0)
+ self.signal.emit(0)
print("After emit.")
class TestBugPYSIDE164(unittest.TestCase):
def testBlockingSignal(self):
- app = QCoreApplication.instance() or QCoreApplication([])
+ app = QCoreApplication.instance() or QCoreApplication([]) # noqa: F841
eventloop = QEventLoop()
emitter = Emitter()
receiver = Receiver(eventloop)
- emitter.connect(emitter, SIGNAL("signal(int)"),
- receiver.receive, Qt.BlockingQueuedConnection)
+ emitter.signal.connect(receiver.receive, Qt.BlockingQueuedConnection)
emitter.start()
retval = eventloop.exec()
emitter.wait(2000)
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import os
+import sys
+import unittest
+
+from pathlib import Path
+sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
+from init_paths import init_test_paths
+init_test_paths(False)
+
+from PySide6.QtCore import QTimer
+
+from helper.usesqapplication import UsesQApplication
+
+
+class TestBugPYSIDE2745(UsesQApplication):
+
+ def setUp(self):
+ UsesQApplication.setUp(self)
+ self.counter = 0
+
+ def fail(self):
+ self.counter += 1
+ raise Exception()
+
+ def test_fail(self):
+ QTimer.singleShot(0, self.fail)
+ QTimer.singleShot(0, self.fail)
+ QTimer.singleShot(1, self.app.quit)
+ self.app.exec()
+ self.assertEqual(self.counter, 2)
+
+
+if __name__ == '__main__':
+ unittest.main()
@unittest.skipIf(is_pypy, "__feature__ cannot yet be used with PyPy")
class ErrormessagesWithFeatures(unittest.TestCase):
probe = "called with wrong argument types"
- probe_miss = "missing signature"
def setUp(self):
qApp or QApplication()
with self.assertRaises(TypeError) as cm:
QApplication.quitOnLastWindowClosed = object
print("\n\n" + cm.exception.args[0])
- self.assertTrue(self.probe_miss in cm.exception.args[0])
+ self.assertTrue(self.probe in cm.exception.args[0])
with self.assertRaises(TypeError) as cm:
qApp.quitOnLastWindowClosed = object
- self.assertTrue(self.probe_miss in cm.exception.args[0])
+ self.assertTrue(self.probe in cm.exception.args[0])
def testCorrectErrorMessagesClassSnakeProp(self):
from __feature__ import snake_case, true_property
with self.assertRaises(TypeError) as cm:
QApplication.quit_on_last_window_closed = object
print("\n\n" + cm.exception.args[0])
- self.assertTrue(self.probe_miss in cm.exception.args[0])
+ self.assertTrue(self.probe in cm.exception.args[0])
with self.assertRaises(TypeError) as cm:
qApp.quit_on_last_window_closed = object
- self.assertTrue(self.probe_miss in cm.exception.args[0])
+ self.assertTrue(self.probe in cm.exception.args[0])
def testDocIsWorking(self):
"""
from PySide6.QtCore import QLine, QPoint, QRect, QSize
+URL = "https://qt.io/"
+
+
class HashTest(unittest.TestCase):
def testInsert(self):
myHash = {}
qdate = QDate.currentDate()
qdatetime = QDateTime.currentDateTime()
qtime = QTime.currentTime()
- qurl = QUrl("http://www.pyside.org")
+ qurl = QUrl(URL)
+ self.assertTrue(qurl.isValid())
qpoint = QPoint(12, 42)
myHash[qdate] = "QDate"
self.assertEqual(l1, l2)
self.assertEqual(hash(l1), hash(l2))
+ def testQTimeHash(self):
+ t1 = QTime(5, 5, 5)
+ t2 = QTime(5, 5, 5)
+ self.assertFalse(t1 is t2)
+ self.assertEqual(t1, t2)
+ self.assertEqual(hash(t1), hash(t2))
+
+ def testQDateHash(self):
+ d1 = QDate(1968, 3, 9)
+ d2 = QDate(1968, 3, 9)
+ self.assertFalse(d1 is d2)
+ self.assertEqual(d1, d2)
+ self.assertEqual(hash(d1), hash(d2))
+
+ def testQDateTimeHash(self):
+ d1 = QDateTime(QDate(1968, 3, 9), QTime(5, 5, 5))
+ d2 = QDateTime(QDate(1968, 3, 9), QTime(5, 5, 5))
+ self.assertFalse(d1 is d2)
+ self.assertEqual(d1, d2)
+ self.assertEqual(hash(d1), hash(d2))
+
+ def testQUrlHash(self):
+ u1 = QUrl(URL)
+ u2 = QUrl(URL)
+ self.assertFalse(u1 is u2)
+ self.assertEqual(u1, u2)
+ self.assertEqual(hash(u1), hash(u2))
+
if __name__ == '__main__':
unittest.main()
-
import ctypes
import os
import pickle
+import struct
import sys
import unittest
init_test_paths(False)
-from PySide6.QtCore import QByteArray, QSettings, QObject, QDataStream, QIODevice
+from PySide6.QtCore import (QByteArray, QSettings, QObject, QDataStream,
+ QIODevice, qCompress, qUncompress)
class QByteArrayTestToNumber(unittest.TestCase):
actual_bytes = bytes(byte_array)
self.assertEqual(orig_bytes, actual_bytes)
+ def testUnpack(self):
+ b = QByteArray(b'\x19\x00\x00\x00\xc4\t\x00\x00')
+ t = struct.unpack('<ii', b)
+ self.assertEqual(len(t), 2)
+ self.assertEqual(t[0], 25)
+ self.assertEqual(t[1], 2500)
+
+
+class QCompressTest(unittest.TestCase):
+ def testQByteArrayCompression(self):
+ """Compress/uncompress a QByteArray."""
+ data = bytes(10 * 'long redundant sentence bla bla', "UTF8")
+ ba = QByteArray(data)
+ compressed = qCompress(ba)
+ self.assertTrue(len(compressed) < len(data))
+ uncompressed = qUncompress(compressed)
+ self.assertEqual(uncompressed, data)
+
+ def testBufferCompression(self):
+ """Compress/uncompress portions of bytes without converting to
+ QByteArray."""
+ data = bytes(10 * 'long redundant sentence bla bla', "UTF8")
+ used_len = int(len(data) / 2)
+ compressed = qCompress(data, used_len, -1)
+ self.assertTrue(len(compressed) < used_len)
+ uncompressed = qUncompress(compressed.data(), len(compressed))
+ self.assertEqual(uncompressed, data[:used_len])
+
if __name__ == '__main__':
unittest.main()
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+'''Test cases for the QIOPipe class'''
+
+from PySide6.QtCore import QIODevice, QIOPipe
+
+import unittest
+
+
+class QIOPipeTest(unittest.TestCase):
+ def setUp(self) -> None:
+ self.pipe = QIOPipe()
+ self.pipe.open(QIODevice.OpenModeFlag.ReadWrite)
+ return super().setUp()
+
+ def tearDown(self) -> None:
+ super().tearDown()
+
+ def ready_read_bytes_written(self):
+ received_data = self.pipe.end2().readAll().data()
+ self.assertEqual(received_data, self.data)
+
+ def test_readyRead(self):
+ self.data = b"Hello, World!"
+ self.pipe.end2().readyRead.connect(self.ready_read_bytes_written)
+ self.pipe.end1().write(self.data)
+
+ def test_bytesWritten(self):
+ self.data = b"Hello, World!"
+ self.pipe.end2().bytesWritten.connect(self.ready_read_bytes_written)
+ self.pipe.end1().write(self.data)
+
+
+if __name__ == '__main__':
+ unittest.main()
lockFile = QLockFile(self._fileName)
self.assertTrue(lockFile.lock())
self.assertTrue(lockFile.isLocked())
+ lock_info = lockFile.getLockInfo();
+ self.assertEqual(len(lock_info), 3)
+ self.assertEqual(lock_info[0], os.getpid())
lockFile.unlock()
o.connect(o2, SIGNAL("bars()"), o.slot)
self.assertTrue(o2.metaObject().indexOfMethod("bars()") > -1)
- #self.assertTrue(o.metaObject().indexOfMethod("bar()") == -1)
- #self.assertTrue(o.metaObject().indexOfMethod("slot()") > -1)
-
- #slot_index = o.metaObject().indexOfMethod("slot()")
-
- #o.connect(o, SIGNAL("foo()"), o2, SIGNAL("bar()"))
- #signal_index = o.metaObject().indexOfMethod("foo()");
-
- #self.assertTrue(slot_index != signal_index)
# PYSIDE-784, plain Qt objects should not have intermediary
# metaObjects.
# PYSIDE-1827, slots with non-QObject object types should work
# (metatypes are registered)
def test_ObjectSlotSignal(self):
- app = QCoreApplication()
+ app = QCoreApplication() # noqa: F841
sender = SemaphoreSender()
receiver = SemaphoreReceiver()
sender.signal.connect(receiver.receiverSlot, Qt.QueuedConnection)
'''Unit tests for QMimeDatabase'''
-import ctypes
import os
import sys
import unittest
s0 = db.mimeTypeForName("application/x-zerosize")
self.assertTrue(s0.isValid())
self.assertEqual(s0.name(), "application/x-zerosize")
- if "en" in QLocale().name():
- self.assertEqual(s0.comment(), "empty document")
+ self.assertTrue(s0.comment())
s0Again = db.mimeTypeForName("application/x-zerosize")
self.assertEqual(s0Again.name(), s0.name())
rdf = db.mimeTypeForName("application/rdf+xml")
self.assertTrue(rdf.isValid())
self.assertEqual(rdf.name(), "application/rdf+xml")
+ self.assertTrue(rdf.comment())
if "en" in QLocale().name():
self.assertEqual(rdf.comment(), "RDF file")
bzip2 = db.mimeTypeForName("application/x-bzip2")
self.assertTrue(bzip2.isValid())
- if "en" in QLocale().name():
- self.assertEqual(bzip2.comment(), "Bzip archive")
+ self.assertTrue(bzip2.comment())
defaultMime = db.mimeTypeForName("application/octet-stream")
self.assertTrue(defaultMime.isValid())
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, SIGNAL, SLOT
+from PySide6.QtCore import QObject, Signal, SIGNAL, SLOT
from helper.usesqapplication import UsesQApplication
class Obj(QObject):
+
+ foo = Signal()
+
def __init__(self):
super().__init__()
self.con_notified = False
UsesQApplication.tearDown(self)
def testBasic(self):
+ sender = Obj()
+ receiver = QObject()
+ sender.destroyed.connect(receiver.deleteLater)
+ self.assertTrue(sender.con_notified)
+ self.assertEqual(sender.signal.methodSignature(), "destroyed()")
+ self.assertTrue(sender.destroyed.disconnect(receiver.deleteLater))
+ self.assertTrue(sender.dis_notified)
+
+ def testBasicString(self):
sender = Obj()
receiver = QObject()
sender.connect(SIGNAL("destroyed()"), receiver, SLOT("deleteLater()"))
# will use the non-cloned method signature, so connecting to destroyed() will actually
# connect to destroyed(QObject*).
self.assertEqual(sender.signal.methodSignature(), "destroyed(QObject*)")
- sender.disconnect(SIGNAL("destroyed()"), receiver, SLOT("deleteLater()"))
+ self.assertTrue(sender.disconnect(SIGNAL("destroyed()"), receiver, SLOT("deleteLater()")))
self.assertTrue(sender.dis_notified)
def testPySignal(self):
sender = Obj()
receiver = QObject()
- sender.connect(SIGNAL("foo()"), receiver, SLOT("deleteLater()"))
+ sender.foo.connect(receiver.deleteLater)
self.assertTrue(sender.con_notified)
- sender.disconnect(SIGNAL("foo()"), receiver, SLOT("deleteLater()"))
+ self.assertTrue(sender.foo.disconnect(receiver.deleteLater))
self.assertTrue(sender.dis_notified)
def testPySlots(self):
sender = Obj()
- receiver = QObject()
- sender.connect(SIGNAL("destroyed()"), cute_slot)
+ sender.destroyed.connect(cute_slot)
self.assertTrue(sender.con_notified)
- sender.disconnect(SIGNAL("destroyed()"), cute_slot)
+ self.assertTrue(sender.destroyed.disconnect(cute_slot))
self.assertTrue(sender.dis_notified)
def testpyAll(self):
sender = Obj()
- receiver = QObject()
- sender.connect(SIGNAL("foo()"), cute_slot)
+ sender.foo.connect(cute_slot)
self.assertTrue(sender.con_notified)
- sender.disconnect(SIGNAL("foo()"), cute_slot)
+ self.assertTrue(sender.foo.disconnect(cute_slot))
self.assertTrue(sender.dis_notified)
self.app.quit()
+class PolymorphicIdFilterObject(QObject):
+ """PYSIDE-2675: Check whether QChildEvent.added() is accessible via PolymorphicId"""
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.added = False
+
+ def event(self, event):
+ self.added = event.added()
+ return False
+
+
class TestQObjectEventFilterPython(UsesQApplication):
'''QObject.eventFilter - Reimplemented in python
Filters 5 TimerEvents and then bypasses the other events to the
self.assertEqual(filtered.times_called, 5)
self.assertEqual(self.obj_filter.events_handled, 5)
+ def testPolymorphicId(self):
+ testObject = PolymorphicIdFilterObject()
+ t2 = QObject(testObject) # noqa: F841
+ self.assertTrue(testObject.added)
+
@unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount")
def testInstallEventFilterRefCountAfterDelete(self):
'''Bug 910 - installEventFilter() increments reference count on target object
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QCoreApplication, QObject, QTimer, SIGNAL, SLOT
+from PySide6.QtCore import QCoreApplication, QObject, QTimer
"""
This is a simple slot test that was updated to use the qApp "macro".
def slot(self):
self.ok = True
- qApp.quit()
+ qApp.quit() # noqa: F821
class slotTest(unittest.TestCase):
def quit_app(self):
- qApp.quit()
+ qApp.quit() # noqa: F821
def testBasic(self):
timer = QTimer()
timer.setInterval(100)
my_obj = objTest()
- my_slot = SLOT("slot()")
- QObject.connect(timer, SIGNAL("timeout()"), my_obj, my_slot)
+ timer.timeout.connect(my_obj.slot)
timer.start(100)
QTimer.singleShot(1000, self.quit_app)
- qApp.exec()
+ qApp.exec() # noqa: F821
self.assertTrue(my_obj.ok)
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, QThread, QTimer, Signal
+from PySide6.QtCore import QObject, QThread, QTimer, Signal, Slot, SLOT
from helper.usesqapplication import UsesQApplication
self.app.exec()
self.assertTrue(self.called)
+ def testSingleShotZero(self):
+ QTimer.singleShot(0, self.callback)
+ self.app.exec()
+ self.assertTrue(self.called)
+
def testSingleShotWithContext(self):
thread = ThreadForContext()
thread.start()
self.assertEqual(self.qthread, thread.qthread)
+class TestSingleShotCallableObject(UsesQApplication):
+ '''Test case for QTimer.singleShot with callable inside an object'''
+
+ def setUp(self):
+ # Acquire resources
+ UsesQApplication.setUp(self)
+ self.watchdog = WatchDog(self)
+
+ def tearDown(self):
+ # Release resources
+ del self.watchdog
+ # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion
+ gc.collect()
+ UsesQApplication.tearDown(self)
+
+ class CallbackObject(QObject):
+ def __init__(self, app) -> None:
+ super().__init__()
+ self.app = app
+
+ @Slot()
+ def func(self):
+ self.called = True
+ self.app.quit()
+
+ def testSingleShotWithObjectAndMember(self):
+ callback = self.CallbackObject(self.app)
+ QTimer.singleShot(100, callback, SLOT("func()"))
+ self.app.exec()
+ self.assertTrue(callback.called)
+
+ def testSingleShotWithObjectAndMemberZero(self):
+ callback = self.CallbackObject(self.app)
+ QTimer.singleShot(0, callback, SLOT("func()"))
+ self.app.exec()
+ self.assertTrue(callback.called)
+
+ def testSingleShotWithCallableInObject(self):
+ callback = self.CallbackObject(self.app)
+ QTimer.singleShot(100, callback.func)
+ self.app.exec()
+ self.assertTrue(callback.called)
+
+ def testSingleShotWithCallableInObjectZero(self):
+ callback = self.CallbackObject(self.app)
+ QTimer.singleShot(0, callback.func)
+ self.app.exec()
+ self.assertTrue(callback.called)
+
+
class SigEmitter(QObject):
sig1 = Signal()
self.app.exec()
self.assertTrue(self.called)
+ def testSingleShotSignalZero(self):
+ emitter = SigEmitter()
+ emitter.sig1.connect(self.callback)
+ QTimer.singleShot(0, emitter.sig1)
+ self.app.exec()
+ self.assertTrue(self.called)
+
if __name__ == '__main__':
unittest.main()
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, SIGNAL, QFile, QThread, QTimer, Qt
+from PySide6.QtCore import Signal, QThread
from helper.usesqapplication import UsesQApplication
class MyThread(QThread):
+ test = Signal(str)
+
def run(self):
- self.emit(SIGNAL("test(const QString&)"), "INdT - PySide")
+ self.test.emit("INdT - PySide")
class TestThreadSignal(UsesQApplication):
def testThread(self):
t = MyThread()
- QObject.connect(t, SIGNAL("test(const QString&)"), self._callback)
+ t.test.connect(self._callback)
t.start()
self.app.exec()
init_test_paths(False)
from PySide6.QtCore import QPointF
-from PySide6.QtGui import QTransform, QPolygonF, QPolygonF
+from PySide6.QtGui import QTransform, QPolygonF, QPolygonF, QQuaternion, QVector3D
class QTransformTest(unittest.TestCase):
self.assertEqual(t1, r2)
+ def testQQuaternion(self):
+ """Test return tuples."""
+ q = QQuaternion(1, 1, 1, 1)
+ self.assertEqual(len(q.getAxisAndAngle()), 2)
+ self.assertEqual(len(q.getEulerAngles()), 3)
+
if __name__ == "__main__":
unittest.main()
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths # noqa: E402
init_test_paths(False)
from helper.timedqguiapplication import TimedQGuiApplication
-from PySide6.support import deprecated
-from PySide6.support.signature import importhandler
-from PySide6 import QtGui
class TestTimedApp(TimedQGuiApplication):
# Simple test of TimedQGuiApplication
self.app.exec()
-
-def fix_for_QtGui(QtGui):
- QtGui.something = 42
-
-
-class TestPatchingFramework(unittest.TestCase):
- """Simple test that verifies that deprecated.py works"""
-
- deprecated.fix_for_QtGui = fix_for_QtGui
-
- def test_patch_works(self):
- something = "something"
- self.assertFalse(hasattr(QtGui, something))
- importhandler.finish_import(QtGui)
- self.assertTrue(hasattr(QtGui, something))
+# deprecated.py is no longer needed.
if __name__ == '__main__':
PYSIDE_TEST(bug_1029.py)
PYSIDE_TEST(groupedproperty.py)
PYSIDE_TEST(listproperty.py)
+PYSIDE_TEST(qmlregistertype_test.py)
PYSIDE_TEST(qqmlapplicationengine_test.py)
PYSIDE_TEST(qqmlnetwork_test.py)
PYSIDE_TEST(qqmlcomponent_test.py)
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths # noqa: E402
init_test_paths(False)
-from PySide6.QtCore import QObject
-from PySide6.QtQml import ListProperty
+from helper.usesqapplication import UsesQApplication # noqa: E402, F401
+
+from PySide6.QtCore import QObject, QUrl, Property, qInstallMessageHandler # noqa: E402
+from PySide6.QtQml import ListProperty, QmlElement # noqa: E402
+from PySide6.QtQuick import QQuickView # noqa: E402
+
+
+QML_IMPORT_NAME = "test.ListPropertyTest"
+QML_IMPORT_MAJOR_VERSION = 1
+
+output_messages = []
+
+
+def message_handler(mode, context, message):
+ global output_messages
+ output_messages.append(f"{message}")
class InheritsQObject(QObject):
pass
-class TestListProperty(unittest.TestCase):
+@QmlElement
+class Person(QObject):
+ def __init__(self, parent=None):
+ super().__init__(parent=None)
+ self._name = ''
+ self._friends = []
+
+ def appendFriend(self, friend):
+ self._friends.append(friend)
+
+ def friendCount(self):
+ return len(self._friends)
+
+ def friend(self, index):
+ return self._friends[index]
+
+ def removeLastItem(self):
+ if len(self._friends) > 0:
+ self._friends.pop()
+
+ def replace(self, index, friend):
+ if 0 <= index < len(self._friends):
+ self._friends[index] = friend
+
+ def clear(self):
+ self._friends.clear()
+
+ @Property(str, final=True)
+ def name(self):
+ return self._name
+
+ @name.setter
+ def name(self, value):
+ self._name = value
+
+ friends = ListProperty(QObject, append=appendFriend, count=friendCount, at=friend,
+ removeLast=removeLastItem, replace=replace, clear=clear)
+
+
+class TestListProperty(UsesQApplication):
def testIt(self):
# Verify that type checking works properly
try:
ListProperty(QObject)
ListProperty(InheritsQObject)
- except:
+ except Exception:
type_check_error = True
self.assertFalse(type_check_error)
method_check_error = False
try:
- ListProperty(QObject, append=None, at=None, count=None, replace=None, clear=None, removeLast=None) # Explicitly setting None
+ ListProperty(QObject, append=None, at=None, count=None, replace=None, clear=None,
+ removeLast=None) # Explicitly setting None
ListProperty(QObject, append=dummyFunc)
ListProperty(QObject, count=dummyFunc, at=dummyFunc)
- except:
+ except Exception:
method_check_error = True
self.assertFalse(method_check_error)
try:
- ListPropery(QObject, append=QObject())
- except:
+ ListProperty(QObject, append=QObject())
+ except Exception:
method_check_error = True
self.assertTrue(method_check_error)
+ def testListPropParameters(self):
+ global output_messages
+ qInstallMessageHandler(message_handler)
+ view = QQuickView()
+ file = Path(__file__).resolve().parent / 'listproperty.qml'
+ self.assertTrue(file.is_file())
+ view.setSource(QUrl.fromLocalFile(file))
+ view.show()
+ self.assertEqual(output_messages[0], "List length: 3")
+ self.assertEqual(output_messages[1], "First element: Alice")
+ self.assertEqual(output_messages[2], "Removing last item: Charlie")
+ self.assertEqual(output_messages[3], "Replacing last item: Bob")
+ self.assertEqual(output_messages[4], "Replaced last item: David")
+ self.assertEqual(output_messages[5], "List length after clearing: 0")
+
if __name__ == '__main__':
unittest.main()
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick 2.0
+import test.ListPropertyTest
+
+Rectangle {
+ width: 360
+ height: 360
+
+ Person {
+ id: person
+ friends: [
+ Person{
+ name: "Alice"
+ },
+ Person{
+ name: "Bob"
+ },
+ Person{
+ name: "Charlie"
+ }
+ ]
+ }
+
+ Person{
+ id: david
+ name: "David"
+ }
+
+ Component.onCompleted: {
+ // Access the length of the list
+ console.log("List length: " + person.friends.length);
+
+ // Access the first element of the list
+ console.log("First element: " + person.friends[0].name);
+
+ // Remove the last item of the list
+ console.log("Removing last item: " + person.friends.pop().name);
+
+ // Repalce the last item of the list
+ console.log("Replacing last item: " + person.friends[person.friends.length - 1].name);
+ person.friends[person.friends.length - 1] = david;
+ console.log("Replaced last item: " + person.friends[person.friends.length - 1].name);
+
+ // Clear the list
+ person.friends = [];
+ console.log("List length after clearing: " + person.friends.length);
+ }
+}
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import os
+import sys
+import unittest
+
+from pathlib import Path
+sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
+from init_paths import init_test_paths
+init_test_paths(False)
+
+from helper.usesqapplication import UsesQApplication
+
+
+from PySide6.QtCore import QCoreApplication, QObject # noqa: F401
+from PySide6.QtQml import QQmlApplicationEngine, qmlRegisterType
+
+
+class BaseClass(QObject):
+ def __init__(self, p=None):
+ super().__init__(p)
+
+
+class ChildClass(BaseClass):
+ def __init__(self, p=None):
+ super().__init__(p)
+
+
+class TestQmlRegisterType(UsesQApplication):
+ """Test the legacy QML register functions."""
+
+ def test(self):
+ qmlRegisterType(BaseClass, 'test', 1, 0, 'BaseClass')
+ qmlRegisterType(ChildClass, 'test', 1, 0, 'ChildClass')
+ # PYSIDE-2709: qmlRegisterType() would set additional class info
+ # on the meta objects for registration which caused another meta
+ # object to be created, breaking inheritance.
+ child = ChildClass()
+ base = BaseClass()
+ self.assertTrue(child.metaObject().inherits(base.metaObject()))
+
+ engine = QQmlApplicationEngine()
+ file = Path(__file__).resolve().parent / 'qmlregistertype_test.qml'
+
+ engine.load(file)
+ rootObjects = engine.rootObjects()
+ self.assertTrue(rootObjects)
+ self.assertTrue(type(rootObjects[0]), ChildClass)
+
+
+if __name__ == '__main__':
+ unittest.main()
--- /dev/null
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import test
+
+ChildClass {
+}
from helper.helper import quickview_errorstring
-from PySide6.QtCore import Property, Signal, QTimer, QUrl, QObject
+from PySide6.QtCore import Property, Signal, QTimer, QUrl, QObject, Slot
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import (qmlRegisterSingletonType, qmlRegisterSingletonInstance,
- QmlElement, QmlSingleton)
+ QmlElement, QmlSingleton, QJSValue)
from PySide6.QtQuick import QQuickView
+
+URI = "Singletons"
+
+
finalResult = 0
+qObjectQmlTypeId = 0
class SingletonQObject(QObject):
return engine.evaluate("new Object({data: 50})")
-QML_IMPORT_NAME = "Singletons"
+QML_IMPORT_NAME = URI
QML_IMPORT_MAJOR_VERSION = 1
data = Property(int, getData, setData)
+@QmlElement
+@QmlSingleton
+class DecoratedSingletonWithCreate(QObject):
+ def __init__(self, data, parent=None):
+ super().__init__(parent)
+ self._data = data
+
+ @staticmethod
+ def create(engine):
+ return DecoratedSingletonWithCreate(400)
+
+ def getData(self):
+ return self._data
+
+ def setData(self, data):
+ self._data = data
+
+ data = Property(int, getData, setData)
+
+
+class TestQuickView(QQuickView):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._singleton_instance_qobject_int = False
+ self._singleton_instance_qobject_str = False
+ self._singleton_instance_jsvalue_int = False
+
+ @Slot()
+ def testSlot(self):
+ engine = self.engine()
+ instance = engine.singletonInstance(qObjectQmlTypeId)
+ if instance is not None and isinstance(instance, QObject):
+ self._singleton_instance_qobject_int = True
+ instance = engine.singletonInstance(URI, 'SingletonQObjectNoCallback')
+ if instance is not None and isinstance(instance, QObject):
+ self._singleton_instance_qobject_str = True
+ instance = engine.singletonInstance(URI, 'SingletonQJSValue')
+ if instance is not None and isinstance(instance, QJSValue):
+ self._singleton_instance_jsvalue_int = True
+ self.close()
+
+
class TestQmlSupport(unittest.TestCase):
def testIt(self):
app = QGuiApplication([])
- qmlRegisterSingletonType(SingletonQObject, 'Singletons', 1, 0, 'SingletonQObjectNoCallback')
- qmlRegisterSingletonType(SingletonQObject, 'Singletons', 1, 0, 'SingletonQObjectCallback',
+ qObjectQmlTypeId = qmlRegisterSingletonType(SingletonQObject, URI, 1, 0,
+ 'SingletonQObjectNoCallback')
+ qmlRegisterSingletonType(SingletonQObject, URI, 1, 0, 'SingletonQObjectCallback',
singletonQObjectCallback)
- qmlRegisterSingletonType('Singletons', 1, 0, 'SingletonQJSValue', singletonQJSValueCallback)
+ qmlRegisterSingletonType(URI, 1, 0, 'SingletonQJSValue', singletonQJSValueCallback)
# Accepts only QObject derived types
l = [1, 2]
with self.assertRaises(TypeError):
- qmlRegisterSingletonInstance(SingletonQObject, 'Singletons', 1, 0, 'SingletonInstance', l)
+ qmlRegisterSingletonInstance(SingletonQObject, URI, 1, 0, 'SingletonInstance', l)
# Modify value on the instance
s = SingletonQObject()
s.setData(99)
- qmlRegisterSingletonInstance(SingletonQObject, 'Singletons', 1, 0, 'SingletonInstance', s)
+ qmlRegisterSingletonInstance(SingletonQObject, URI, 1, 0, 'SingletonInstance', s)
- view = QQuickView()
+ view = TestQuickView()
file = Path(__file__).resolve().parent / 'registersingletontype.qml'
self.assertTrue(file.is_file())
view.setSource(QUrl.fromLocalFile(file))
self.assertTrue(view.rootObject(), quickview_errorstring(view))
view.resize(200, 200)
view.show()
- QTimer.singleShot(250, view.close)
+ QTimer.singleShot(250, view.testSlot)
app.exec()
- self.assertEqual(finalResult, 499)
+ self.assertEqual(finalResult, 899)
+ self.assertTrue(view._singleton_instance_qobject_int)
+ self.assertTrue(view._singleton_instance_qobject_str)
+ self.assertTrue(view._singleton_instance_jsvalue_int)
-if __name__ == '__main__':
- unittest.main()
+if __name__ == '__main__': unittest.main()
SingletonQObjectCallback.data += SingletonQObjectNoCallback.data
+ SingletonQJSValue.data
+ SingletonInstance.data
- + DecoratedSingletonQObject.data;
+ + DecoratedSingletonQObject.data + DecoratedSingletonWithCreate.data;
}
}
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+PYSIDE_TEST(quicktestmainwithsetup/tst_quicktestmainwithsetup.py)
--- /dev/null
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import QtQuick 2.0
+import QtTest 1.2
+
+import QmlRegisterTypeCppModule 1.0
+import ImportPathQmlModule 1.0
+
+TestCase {
+ name: "setup"
+
+ QmlRegisterTypeCppType {}
+ ImportPathQmlType {}
+
+ function initTestCase()
+ {
+ verify(qmlEngineAvailableCalled)
+ }
+}
--- /dev/null
+import QtQuick 2.0
+
+Item {}
--- /dev/null
+module ImportPathQmlModule
+ImportPathQmlType 1.0 ImportPathQmlType.qml
--- /dev/null
+{
+ "files": ["tst_quicktestmainwithsetup.py", "data/tst_setup.qml",
+ "imports/ImportPathQmlModule/ImportPathQmlType.qml"]
+}
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import os
+import sys
+
+from pathlib import Path
+sys.path.append(os.fspath(Path(__file__).resolve().parents[2]))
+from init_paths import init_test_paths
+init_test_paths(False)
+
+from pathlib import Path
+from PySide6.QtCore import QObject, Slot
+from PySide6.QtQml import QQmlEngine, qmlRegisterType
+from PySide6.QtQuickTest import QUICK_TEST_MAIN_WITH_SETUP
+
+
+"""Copy of the equivalent test in qtdeclarative."""
+
+
+class QmlRegisterTypeCppType(QObject):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+
+
+class CustomTestSetup(QObject):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+
+ @Slot(QQmlEngine)
+ def qmlEngineAvailable(self, qmlEngine):
+ # Test that modules are successfully imported by the TestCaseCollector
+ # that parses the QML files (but doesn't run them). For that to happen,
+ # qmlEngineAvailable() must be called before TestCaseCollector does its
+ # thing.
+ qmlRegisterType(QmlRegisterTypeCppType, "QmlRegisterTypeCppModule", 1, 0,
+ "QmlRegisterTypeCppType")
+ import_dir = Path(__file__).parent / "imports"
+ qmlEngine.addImportPath(os.fspath(import_dir))
+ qmlEngine.rootContext().setContextProperty("qmlEngineAvailableCalled", True)
+
+
+data_dir = Path(__file__).parent / "data"
+exitCode = QUICK_TEST_MAIN_WITH_SETUP("qquicktestsetup", CustomTestSetup, sys.argv,
+ os.fspath(data_dir))
+sys.exit(exitCode)
PYSIDE_TEST(qapp_test.py)
PYSIDE_TEST(qapplication_test.py)
PYSIDE_TEST(qapplication_exit_segfault_test.py)
+PYSIDE_TEST(qdialog_test.py)
PYSIDE_TEST(qdynamic_signal.py)
# TODO: This passes, but requires manual button clicking (at least on mac)
#PYSIDE_TEST(qfontdialog_test.py)
--- /dev/null
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import os
+import sys
+import unittest
+import weakref
+
+from pathlib import Path
+sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
+from init_paths import init_test_paths
+init_test_paths(False)
+
+from PySide6.QtCore import Slot, QTimer
+from PySide6.QtWidgets import QDialog, QMainWindow
+from helper.timedqapplication import TimedQApplication
+
+
+class Window(QMainWindow):
+ def __init__(self):
+ super().__init__()
+ self.setWindowTitle("Main")
+ self.dialog = None
+
+ @Slot()
+ def execDialog(self):
+ dialog = QDialog(self)
+ self.dialog = weakref.ref(dialog)
+ dialog.setWindowTitle("Dialog")
+ dialog.setMinimumWidth(200)
+ QTimer.singleShot(500, dialog.reject)
+ dialog.exec()
+ self.close()
+
+
+class DialogExecTest(TimedQApplication):
+ """Test whether the parent-child relationship (dialog/main window) is removed when
+ using QDialog.exec() (instead show()), preventing the dialog from leaking."""
+
+ def setUp(self):
+ super().setUp(10000)
+ self._window = Window()
+
+ def testExec(self):
+ self._window.show()
+ QTimer.singleShot(500, self._window.execDialog)
+ self.app.exec()
+ self.assertTrue(self._window.dialog() is None)
+
+
+if __name__ == '__main__':
+ unittest.main()
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+"""
+Time a repeated Python run
+--------------------------
+
+Usage: python3 lazytiming.py # uses PySide6
+ python3 lazytiming.py <any arg> # uses PyQt6
+
+It runs the same python for the testing.
+
+Actually comparing PySide6 and PyQt6 in action:
+
+ PYSIDE6_OPTION_LAZY=0 python3 sources/pyside6/tests/manually/lazytiming.py # normal
+ PYSIDE6_OPTION_LAZY=1 python3 sources/pyside6/tests/manually/lazytiming.py # faster
+ python3 sources/pyside6/tests/manually/lazytiming.py xxx # PyQt
+"""
+import subprocess
+import sys
+
+from timeit import default_timer as timer
+
+repeats = 100
+test1 = "PySide6"
+test2 = "PyQt6"
+
+test = test2 if sys.argv[1:] else test1
+cmd = [sys.executable, "-c", f"from {test} import QtCore, QtGui, QtWidgets"]
+
+print(f"{repeats} * {test}")
+
+subprocess.call(cmd) # warmup
+start_time = timer()
+for idx in range(repeats):
+ subprocess.call(cmd)
+stop_time = timer()
+print(f"time per run = {(stop_time - start_time) / repeats}")
${CMAKE_CURRENT_BINARY_DIR}/testbinding/testview_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/testbinding/testbinding_module_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/testbinding/testqvariantenum_wrapper.cpp
+${CMAKE_CURRENT_BINARY_DIR}/testbinding/qvariantholder_wrapper.cpp
)
# Get per module include dirs.
from helper.usesqapplication import UsesQApplication
from PySide6 import QtCore, QtGui, QtWidgets
-from PySide6.QtWidgets import QMainWindow, QLabel
+from PySide6.QtWidgets import QMainWindow, QLabel, QWidget
def xprint(*args, **kw):
xprint('C: after init')
-# mro ('F', 'D', 'QCursor', 'E', 'QLabel', 'QFrame', 'QWidget', 'QObject', 'QPaintDevice', 'Object', 'object')
+# mro ('F', 'D', 'QCursor', 'E', 'QLabel', 'QFrame', 'QWidget', 'QObject',
+# 'QPaintDevice', 'Object', 'object')
class D(QtGui.QCursor):
def __init__(self, anna=77, **kw):
xprint(f'D: before init kw = {kw}')
xprint('F: after init')
-# mro ('I', 'G', 'QTextDocument', 'H', 'QLabel', 'QFrame', 'QWidget', 'QObject', 'QPaintDevice', 'Object', 'object')
+# mro ('I', 'G', 'QTextDocument', 'H', 'QLabel', 'QFrame', 'QWidget', 'QObject',
+# 'QPaintDevice', 'Object', 'object')
# Similar, but this time we want to reach `H` without support from `super`.
class G(QtGui.QTextDocument):
pass
xprint('H: after init')
-class I(G, H, QtWidgets.QLabel):
+class II(G, H, QtWidgets.QLabel):
pass
def testGHI(self):
xprint()
- res = I(age=7)
+ res = II(age=7)
self.assertEqual(res.age, 7)
xprint()
MainWindow()
+# PYSIDE-2654: Additional missing init test.
+# This must work if no __init__ is defined (Ui_Form)
+class Ui_Form(object):
+ pass
+
+
+class Mixin:
+ def __init__(self, **kwargs) -> None:
+ super().__init__(**kwargs)
+
+
+class Card(Mixin, QWidget):
+ def __init__(self, parent=None) -> None:
+ super().__init__(parent=parent)
+
+
+class Demo(Card, Ui_Form):
+ def __init__(self) -> None:
+ super().__init__()
+
+
+class MissingInitFunctionTest(UsesQApplication):
+ def testMissing(self):
+ Demo()
+ # Tests if this works. Would crash without the extra
+ # check for object.__init__
+
+
if __name__ == "__main__":
unittest.main()
return Qt.Orientation.Vertical
def channelingEnum(self, rval_enum):
- return (isinstance(rval_enum, enum.Enum) and
- rval_enum == Qt.Orientation.Vertical)
+ return (isinstance(rval_enum, enum.Enum)
+ and rval_enum == Qt.Orientation.Vertical)
class QVariantTest(UsesQApplication):
QAction().setShortcut(Qt.CTRL | Qt.AltModifier | Qt.Key_B)
QAction().setShortcut(QKeySequence(QKeyCombination(Qt.CTRL | Qt.Key_B)))
QKeySequence(Qt.CTRL | Qt.Key_Q)
- # Issues a warning but works as well
- QKeySequence(Qt.CTRL + Qt.Key_Q)
def testEnum(self):
# Testing C++ class
# __repr__ should use the operator<<(QDebug,...) implementation
self.assertEqual(str(t), "TestObject2WithNamespace(injected_repr)")
+ def testLatin1StringField(self):
+ self.assertEqual(TestObject.LATIN1_TEST_FIELD, "test")
+
+ def testLatin1Setter(self):
+ to = TestObject(123)
+ value = "test"
+ to.setQLatin1String(value)
+ self.assertEqual(to.qLatin1String(), value)
+
if __name__ == '__main__':
unittest.main()
init_test_paths(True)
from testbinding import TestObject
-from PySide6.QtCore import QObject, SIGNAL
+from PySide6.QtCore import Qt
'''Tests the behaviour of signals with default values.'''
self.assertTrue(self.void_called)
self.assertTrue(self.bool_called)
+ def testFlagsSignal(self):
+ test_value = Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignBottom
+
+ def callbackAlignmentFlags(alignment):
+ self.alignment_flags_called = alignment
+
+ self.obj.flagsSignal.connect(callbackAlignmentFlags)
+ self.obj.emitFlagsSignal(test_value)
+ self.assertTrue(self.alignment_flags_called)
+ self.assertEqual(self.alignment_flags_called, test_value)
+
def testConnectOldStyleEmitVoidSignal(self):
def callbackVoid():
self.void_called = True
if __name__ == '__main__':
unittest.main()
-
emit signalWithTypedefValue(TypedefValue(value));
}
+void TestObject::emitSignalWithContainerTypedefValue(const IntList &il)
+{
+ emit signalWithContainerTypedefValue(il);
+}
+
+void TestObject::emitFlagsSignal(Qt::Alignment alignment)
+{
+ emit flagsSignal(alignment);
+}
+
+void TestObject::setQLatin1String(QLatin1String v)
+{
+ m_qLatin1String = v;
+}
+
+QString TestObject::qLatin1String() const
+{
+ return m_qLatin1String;
+}
+
QDebug operator<<(QDebug dbg, TestObject& testObject)
{
QDebugStateSaver saver(dbg);
#include <QtWidgets/QApplication>
+#include <QtCore/QList>
#include <QtCore/QObject>
#include <QtCore/QMetaType>
#include <QtCore/QVariant>
QT_FORWARD_DECLARE_CLASS(QDebug)
+using IntList = QList<int>;
+
class IntValue
{
public:
void emitSignalWithDefaultValue_bool();
void emitSignalWithTypedefValue(int value);
+ void emitSignalWithContainerTypedefValue(const IntList &il);
+
+ void emitFlagsSignal(Qt::Alignment alignment);
+
+ static constexpr auto LATIN1_TEST_FIELD = QLatin1StringView("test");
+
+ void setQLatin1String(QLatin1String v);
+ QString qLatin1String() const;
signals:
void idValue(int newValue);
void childrenChanged(const QList<QObject*>&);
void signalWithDefaultValue(bool value = false);
void signalWithTypedefValue(TypedefValue value);
+ void signalWithContainerTypedefValue(const IntList &il);
+ void flagsSignal(Qt::Alignment alignment);
private:
int m_idValue;
QList<QObject*> m_children;
+ QString m_qLatin1String;
};
PYSIDETEST_API QDebug operator<<(QDebug dbg, TestObject &testObject);
QVariant m_enum;
};
+class PYSIDETEST_API QVariantHolder // modeled after Q3DParameter, test QVariant conversion
+{
+public:
+ void setValue(QVariant v) { m_variant = v; }
+ QVariant value() const { return m_variant; }
+
+private:
+ QVariant m_variant;
+};
+
#endif // TESTQVARIANT_H
from init_paths import init_test_paths
init_test_paths(True)
-from PySide6.QtCore import QObject
+from PySide6.QtCore import QObject, Slot
from testbinding import TestObject
def slot(self, value):
self.received = value
+ @Slot("IntList")
+ def containerSlot(self, value):
+ self.received = value
+
class TypedefSignal(unittest.TestCase):
obj.emitSignalWithTypedefValue(2)
self.assertEqual(receiver.received.value, 2)
+ def testContainerTypedef(self):
+ obj = TestObject(0)
+ receiver = Receiver()
+
+ test_list = [1, 2]
+ obj.signalWithContainerTypedefValue.connect(receiver.containerSlot)
+ obj.emitSignalWithContainerTypedefValue(test_list)
+ self.assertEqual(receiver.received, test_list)
+
if __name__ == '__main__':
unittest.main()
<object-type name="SharedPointerTestbench"/>
+ <value-type name="QVariantHolder"/>
+
<smart-pointer-type name="QSharedPointer" type="shared" getter="data"
reset-method="reset"/>
# distro package, ASAP! The distro has been extracted from Python,
# because it changes more often than the Python version.
distribution = []
- try:
- import distro
- distribution = distro.linux_distribution()
- except ImportError:
- # platform.linux_distribution() was removed in 3.8
- if sys.version_info[:2] < (3, 8):
- import platform
- distribution = platform.linux_distribution()
+ import distro
+ distribution = distro.linux_distribution()
if distribution:
return "".join(distribution[:2]).lower()
warnings.warn('Cannot determine Linux distribution, please install distro',
PYSIDE_TEST(multiple_connections_gui_test.py)
PYSIDE_TEST(multiple_connections_test.py)
PYSIDE_TEST(pysignal_test.py)
+PYSIDE_TEST(qobject_callable_connect_test.py)
PYSIDE_TEST(qobject_destroyed_test.py)
PYSIDE_TEST(qobject_receivers_test.py)
PYSIDE_TEST(qobject_sender_test.py)
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, SIGNAL
+from PySide6.QtCore import QObject, Signal
+
+
+class Sender(QObject):
+ the_signal = Signal(int, int, int)
class ArgsDontMatch(unittest.TestCase):
def testConnectSignalToSlotWithLessArgs(self):
self.ok = False
- obj1 = QObject()
- QObject.connect(obj1, SIGNAL('the_signal(int, int, int)'), self.callback)
- obj1.emit(SIGNAL('the_signal(int, int, int)'), 1, 2, 3)
+ obj1 = Sender()
+ obj1.the_signal.connect(self.callback)
+ obj1.the_signal.emit(1, 2, 3)
self.assertTrue(self.ok)
if __name__ == '__main__':
unittest.main()
-
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, SIGNAL
+from PySide6.QtCore import QObject, Signal
MAX_LOOPS = 5
MAX_OBJECTS = 200
-class Dummy(object):
- def __init__(self, parent):
- self._parent = parent
-
- def callback(self):
- self._called = True
+class Sender(QObject):
+ fire = Signal()
class MultipleSlots(unittest.TestCase):
def myCB(self):
self._count += 1
- """
- def testUnboundSignal(self):
- o = QObject()
- self._count = 0
- for i in range(MAX_OBJECTS):
- QObject.connect(o, SIGNAL("fire()"), lambda: self.myCB())
-
- o.emit(SIGNAL("fire()"))
- self.assertEqual(self._count, MAX_OBJECTS)
-
- """
def testDisconnectCleanup(self):
for c in range(MAX_LOOPS):
self._count = 0
self._senders = []
for i in range(MAX_OBJECTS):
- o = QObject()
- QObject.connect(o, SIGNAL("fire()"), lambda: self.myCB())
+ o = Sender()
+ o.fire.connect(lambda: self.myCB())
self._senders.append(o)
- o.emit(SIGNAL("fire()"))
+ o.fire.emit()
self.assertEqual(self._count, MAX_OBJECTS)
if __name__ == '__main__':
unittest.main()
-
-
if __name__ == '__main__':
unittest.main()
-
def testNoLeaks_ConnectAndDisconnect(self):
self._called = None
- app = QApplication([])
+ app = QApplication([]) # noqa: F841
o = QTreeView()
o.setModel(QStandardItemModel())
o.selectionModel().destroyed.connect(self.callback)
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, Slot, SIGNAL, SLOT
+from PySide6.QtCore import QObject, Slot, Signal
+
+
+class Sender(QObject):
+ mySignal = Signal()
class MyObject(QObject):
self.assertTrue(m.indexOfSlot('mySlot4(QString,int)') > 0)
def testEmission(self):
+ sender = Sender()
o = MyObject()
- o.connect(SIGNAL("mySignal()"), o, SLOT("mySlot()"))
- o.emit(SIGNAL("mySignal()"))
+ sender.mySignal.connect(o.mySlot)
+ sender.mySignal.emit()
self.assertTrue(o._slotCalledCount == 1)
def testResult(self):
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, SIGNAL
+from PySide6.QtCore import QObject
class InvalidCallback(unittest.TestCase):
def testIntegerCb(self):
# Test passing an int as callback to QObject.connect
- self.assertRaises(TypeError, QObject.connect, self.obj,
- SIGNAL('destroyed()'), 42)
+ self.assertRaises(TypeError, self.obj.destroyed.connect, 42)
if __name__ == '__main__':
unittest.main()
-
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, SIGNAL
-
-try:
- from PySide6.QtWidgets import QSpinBox, QPushButton
- hasQtGui = True
-except ImportError:
- hasQtGui = False
+from PySide6.QtWidgets import QSpinBox, QPushButton
from helper.usesqapplication import UsesQApplication
-if hasQtGui:
- class Control:
- def __init__(self):
- self.arg = False
-
- class QtGuiSigLambda(UsesQApplication):
-
- def testButton(self):
- # Connecting a lambda to a QPushButton.clicked()
- obj = QPushButton('label')
- ctr = Control()
- func = lambda: setattr(ctr, 'arg', True)
- obj.clicked.connect(func)
- obj.click()
- self.assertTrue(ctr.arg)
- QObject.disconnect(obj, SIGNAL('clicked()'), func)
-
- def testSpinButton(self):
- # Connecting a lambda to a QPushButton.clicked()
- obj = QSpinBox()
- ctr = Control()
- arg = 444
- func = lambda x: setattr(ctr, 'arg', 444)
- obj.valueChanged.connect(func)
- obj.setValue(444)
- self.assertEqual(ctr.arg, arg)
- QObject.disconnect(obj, SIGNAL('valueChanged(int)'), func)
+
+class Control:
+ def __init__(self):
+ self.arg = False
+
+
+class QtWidgetsSigLambda(UsesQApplication):
+
+ def testButton(self):
+ # Connecting a lambda to a QPushButton.clicked()
+ obj = QPushButton('label')
+ ctr = Control()
+ func = lambda: setattr(ctr, 'arg', True) # noqa: E731
+ obj.clicked.connect(func)
+ obj.click()
+ self.assertTrue(ctr.arg)
+ self.assertTrue(obj.clicked.disconnect(func))
+
+ def testSpinButton(self):
+ # Connecting a lambda to a QPushButton.clicked()
+ obj = QSpinBox()
+ ctr = Control()
+ arg = 444
+ func = lambda x: setattr(ctr, 'arg', 444) # noqa: E731
+ obj.valueChanged.connect(func)
+ obj.setValue(444)
+ self.assertEqual(ctr.arg, arg)
+ self.assertTrue(obj.valueChanged.disconnect(func))
+
if __name__ == '__main__':
unittest.main()
import os
import sys
import unittest
+import weakref
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, SIGNAL, QProcess
+from PySide6.QtCore import QCoreApplication, QObject, Signal, SIGNAL, QProcess
from helper.usesqapplication import UsesQApplication
-class Dummy(QObject):
+class Sender(QObject):
+ void_signal = Signal()
+ int_signal = Signal(int)
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._delayed_int = 0
+
+ def emit_void(self):
+ self.void_signal.emit()
+
+ def emit_int(self, v):
+ self.int_signal.emit(v)
+
+
+class Receiver(QObject):
def __init__(self, *args):
super().__init__(*args)
def testSimplePythonSignalNoArgs(self):
# Connecting a lambda to a simple python signal without arguments
- obj = Dummy()
- QObject.connect(obj, SIGNAL('foo()'),
- lambda: setattr(obj, 'called', True))
- obj.emit(SIGNAL('foo()'))
- self.assertTrue(obj.called)
+ receiver = Receiver()
+ sender = Sender()
+ sender.void_signal.connect(lambda: setattr(receiver, 'called', True))
+ sender.emit_void()
+ self.assertTrue(receiver.called)
def testSimplePythonSignal(self):
# Connecting a lambda to a simple python signal witharguments
- obj = Dummy()
+ receiver = Receiver()
+ sender = Sender()
+ arg = 42
+ sender.int_signal.connect(lambda x: setattr(receiver, 'arg', arg))
+ sender.emit_int(arg)
+ self.assertEqual(receiver.arg, arg)
+
+ def testSimplePythonSignalNoArgsString(self):
+ # Connecting a lambda to a simple python signal without arguments
+ receiver = Receiver()
+ sender = Sender()
+ QObject.connect(sender, SIGNAL('void_signal()'),
+ lambda: setattr(receiver, 'called', True))
+ sender.emit_void()
+ self.assertTrue(receiver.called)
+
+ def testSimplePythonSignalString(self):
+ # Connecting a lambda to a simple python signal witharguments
+ receiver = Receiver()
+ sender = Sender()
arg = 42
- QObject.connect(obj, SIGNAL('foo(int)'),
- lambda x: setattr(obj, 'arg', 42))
- obj.emit(SIGNAL('foo(int)'), arg)
- self.assertEqual(obj.arg, arg)
+ QObject.connect(sender, SIGNAL('int_signal(int)'),
+ lambda x: setattr(receiver, 'arg', arg))
+ sender.emit_int(arg)
+ self.assertEqual(receiver.arg, arg)
class QtSigLambda(UsesQApplication):
qapplication = True
- def testNoArgs(self):
- '''Connecting a lambda to a signal without arguments'''
- proc = QProcess()
- dummy = Dummy()
- QObject.connect(proc, SIGNAL('started()'),
- lambda: setattr(dummy, 'called', True))
- proc.start(sys.executable, ['-c', '""'])
- proc.waitForFinished()
- self.assertTrue(dummy.called)
-
def testWithArgs(self):
- '''Connecting a lambda to a signal with arguments'''
+ '''Connecting a lambda to a signal with and without arguments'''
proc = QProcess()
- dummy = Dummy()
- QObject.connect(proc, SIGNAL('finished(int)'),
- lambda x: setattr(dummy, 'called', x))
+ dummy = Receiver()
+ proc.started.connect(lambda: setattr(dummy, 'called', True))
+ proc.finished.connect(lambda x: setattr(dummy, 'exit_code', x))
+
proc.start(sys.executable, ['-c', '""'])
- proc.waitForFinished()
- self.assertEqual(dummy.called, proc.exitCode())
+ self.assertTrue(proc.waitForStarted())
+ self.assertTrue(proc.waitForFinished())
+
+ self.assertTrue(dummy.called)
+ self.assertEqual(dummy.exit_code, proc.exitCode())
+
+ def testRelease(self):
+ """PYSIDE-2646: Test whether main thread target slot lambda/methods
+ (and their captured objects) are released by the signal manager
+ after a while."""
+
+ def do_connect(sender):
+ receiver = Receiver()
+ sender.void_signal.connect(lambda: setattr(receiver, 'called', True))
+ return receiver
+
+ sender = Sender()
+ receiver = weakref.ref(do_connect(sender))
+ sender.emit_void()
+ self.assertTrue(receiver().called)
+ del sender
+ for i in range(3):
+ if not receiver():
+ break
+ QCoreApplication.processEvents()
+ self.assertFalse(receiver())
if __name__ == '__main__':
import os
import sys
import unittest
-import weakref
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
class Emitter(QObject):
my_signal = Signal(object)
- emitter = Emitter()
+ emitter = Emitter() # noqa: F841
if __name__ == '__main__':
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-from functools import partial
import os
-import random
import sys
import unittest
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, SIGNAL
-
-try:
- from PySide6.QtWidgets import QPushButton, QSpinBox
- hasQtGui = True
-except ImportError:
- hasQtGui = False
+from PySide6.QtWidgets import QPushButton, QSpinBox
from helper.basicpyslotcase import BasicPySlotCase
from helper.usesqapplication import UsesQApplication
-class MultipleSignalConnections(unittest.TestCase):
- '''Base class for multiple signal connection testing'''
+class QtGuiMultipleSlots(UsesQApplication):
+ '''Multiple connections to QtGui signals'''
- def run_many(self, sender, signal, emitter, receivers, args=None):
+ def run_many(self, signal, emitter, receivers, args=None):
"""Utility method to connect a list of receivers to a signal.
sender - QObject that will emit the signal
signal - string with the signal signature
for rec in receivers:
rec.setUp()
- QObject.connect(sender, SIGNAL(signal), rec.cb)
+ signal.connect(rec.cb)
rec.args = tuple(args)
emitter(*args)
for rec in receivers:
self.assertTrue(rec.called)
+ def testButtonClick(self):
+ """Multiple connections to QPushButton.clicked()"""
+ sender = QPushButton('button')
+ receivers = [BasicPySlotCase() for x in range(30)]
+ self.run_many(sender.clicked, sender.click, receivers)
-if hasQtGui:
- class QtGuiMultipleSlots(UsesQApplication, MultipleSignalConnections):
- '''Multiple connections to QtGui signals'''
-
- def testButtonClick(self):
- """Multiple connections to QPushButton.clicked()"""
- sender = QPushButton('button')
- receivers = [BasicPySlotCase() for x in range(30)]
- self.run_many(sender, 'clicked()', sender.click, receivers)
+ def testSpinBoxValueChanged(self):
+ """Multiple connections to QSpinBox.valueChanged(int)"""
+ sender = QSpinBox()
+ # FIXME if number of receivers if higher than 50, segfaults
+ receivers = [BasicPySlotCase() for x in range(10)]
+ self.run_many(sender.valueChanged, sender.setValue,
+ receivers, (1,))
- def testSpinBoxValueChanged(self):
- """Multiple connections to QSpinBox.valueChanged(int)"""
- sender = QSpinBox()
- # FIXME if number of receivers if higher than 50, segfaults
- receivers = [BasicPySlotCase() for x in range(10)]
- self.run_many(sender, 'valueChanged(int)', sender.setValue,
- receivers, (1,))
if __name__ == '__main__':
unittest.main()
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, SIGNAL, QProcess
+from PySide6.QtCore import QObject, Signal, QProcess
from helper.basicpyslotcase import BasicPySlotCase
from helper.usesqapplication import UsesQApplication
class MultipleSignalConnections(unittest.TestCase):
'''Base class for multiple signal connection testing'''
- def run_many(self, sender, signal, emitter, receivers, args=None):
+ def run_many(self, signal, emitter, receivers, args=None):
"""Utility method to connect a list of receivers to a signal.
sender - QObject that will emit the signal
signal - string with the signal signature
args = tuple()
for rec in receivers:
rec.setUp()
- self.assertTrue(QObject.connect(sender, SIGNAL(signal), rec.cb))
+ self.assertTrue(signal.connect(rec.cb))
rec.args = tuple(args)
emitter(*args)
def testPythonSignal(self):
"""Multiple connections to a python signal (short-circuit)"""
- class Dummy(QObject):
- pass
+ class Sender(QObject):
- sender = Dummy()
+ foobar = Signal(int)
+
+ sender = Sender()
receivers = [BasicPySlotCase() for x in range(10)]
- self.run_many(sender, 'foobar(int)', partial(sender.emit,
- SIGNAL('foobar(int)')), receivers, (0, ))
+ self.run_many(sender.foobar, partial(sender.foobar.emit),
+ receivers, (0, ))
class QProcessMultipleSlots(UsesQApplication, MultipleSignalConnections):
def start_proc(*args):
sender.start(sys.executable, ['-c', '""'])
- sender.waitForFinished()
+ self.assertTrue(sender.waitForStarted())
+ self.assertTrue(sender.waitForFinished())
- self.run_many(sender, 'started()', start_proc, receivers)
+ self.run_many(sender.started, start_proc, receivers)
def testQProcessFinished(self):
'''Multiple connections to QProcess.finished(int)'''
def start_proc(*args):
sender.start(sys.executable, ['-c', '""'])
- sender.waitForFinished()
+ self.assertTrue(sender.waitForStarted())
+ self.assertTrue(sender.waitForFinished())
- self.run_many(sender, 'finished(int)', start_proc, receivers, (0,))
+ self.run_many(sender.finished, start_proc, receivers, (0, QProcess.ExitStatus.NormalExit))
if __name__ == '__main__':
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, SIGNAL, SLOT, Qt
-
-try:
- from PySide6.QtWidgets import QSpinBox, QApplication, QWidget
- hasQtGui = True
-except ImportError:
- hasQtGui = False
+from PySide6.QtCore import QObject, Signal, Qt
+from PySide6.QtWidgets import QSpinBox, QApplication, QWidget # noqa: F401
from helper.usesqapplication import UsesQApplication
-class Dummy(QObject):
- """Dummy class used in this test."""
+TEST_LIST = ["item1", "item2", "item3"]
+
+
+class Sender(QObject):
+ """Sender class used in this test."""
+
+ foo = Signal()
+ foo_int = Signal(int)
+ dummy = Signal(str)
+ dummy2 = Signal(str, list)
+
def __init__(self, parent=None):
- QObject.__init__(self, parent)
+ super().__init__(parent)
def callDummy(self):
- self.emit(SIGNAL("dummy(PyObject)"), "PyObject")
+ self.dummy.emit("PyObject")
def callDummy2(self):
- lst = []
- lst.append("item1")
- lst.append("item2")
- lst.append("item3")
- self.emit(SIGNAL("dummy2(PyObject, PyObject)"), "PyObject0", lst)
+ self.dummy2.emit("PyObject0", TEST_LIST)
class PyObjectType(UsesQApplication):
def mySlot2(self, arg0, arg1):
self.assertEqual(arg0, "PyObject0")
- self.assertEqual(arg1[0], "item1")
- self.assertEqual(arg1[1], "item2")
- self.assertEqual(arg1[2], "item3")
+ self.assertEqual(arg1, TEST_LIST)
self.callCount += 1
if self.running:
self.app.quit()
def setUp(self):
- super(PyObjectType, self).setUp()
+ super().setUp()
self.callCount = 0
self.running = False
def testWithOneArg(self):
- o = Dummy()
- o.connect(SIGNAL("dummy(PyObject)"), self.mySlot)
+ o = Sender()
+ o.dummy.connect(self.mySlot)
o.callDummy()
self.assertEqual(self.callCount, 1)
def testWithTwoArg(self):
- o = Dummy()
- o.connect(SIGNAL("dummy2(PyObject,PyObject)"), self.mySlot2)
+ o = Sender()
+ o.dummy2.connect(self.mySlot2)
o.callDummy2()
self.assertEqual(self.callCount, 1)
def testAsyncSignal(self):
self.called = False
self.running = True
- o = Dummy()
- o.connect(SIGNAL("dummy2(PyObject,PyObject)"), self.mySlot2, Qt.QueuedConnection)
+ o = Sender()
+ o.dummy2.connect(self.mySlot2, Qt.QueuedConnection)
o.callDummy2()
self.app.exec()
self.assertEqual(self.callCount, 1)
def testTwice(self):
self.called = False
self.running = True
- o = Dummy()
- o.connect(SIGNAL("dummy2(PyObject,PyObject)"), self.mySlot2, Qt.QueuedConnection)
+ o = Sender()
+ o.dummy2.connect(self.mySlot2, Qt.QueuedConnection)
o.callDummy2()
o.callDummy2()
self.app.exec()
def tearDown(self):
try:
del self.args
- except:
+ except: # noqa: E722
pass
# PYSIDE-535: Need to collect garbage in PyPy to trigger deletion
gc.collect()
def testNoArgs(self):
"""Python signal and slots without arguments"""
- obj1 = Dummy()
+ obj1 = Sender()
- QObject.connect(obj1, SIGNAL('foo()'), self.callback)
+ obj1.foo.connect(self.callback)
self.args = tuple()
- obj1.emit(SIGNAL('foo()'), *self.args)
+ obj1.foo.emit(*self.args)
self.assertTrue(self.called)
def testWithArgs(self):
"""Python signal and slots with integer arguments"""
- obj1 = Dummy()
+ obj1 = Sender()
- QObject.connect(obj1, SIGNAL('foo(int)'), self.callback)
+ obj1.foo_int.connect(self.callback)
self.args = (42,)
- obj1.emit(SIGNAL('foo(int)'), *self.args)
+ obj1.foo_int.emit(*self.args)
self.assertTrue(self.called)
def testDisconnect(self):
- obj1 = Dummy()
+ obj1 = Sender()
- QObject.connect(obj1, SIGNAL('foo(int)'), self.callback)
- QObject.disconnect(obj1, SIGNAL('foo(int)'), self.callback)
+ obj1.foo_int.connect(self.callback)
+ self.assertTrue(obj1.foo_int.disconnect(self.callback))
self.args = (42, )
- obj1.emit(SIGNAL('foo(int)'), *self.args)
+ obj1.foo_int.emit(*self.args)
self.assertTrue(not self.called)
-if hasQtGui:
- class SpinBoxPySignal(UsesQApplication):
- """Tests the connection of python signals to QSpinBox qt slots."""
+class SpinBoxPySignal(UsesQApplication):
+ """Tests the connection of python signals to QSpinBox qt slots."""
- def setUp(self):
- super(SpinBoxPySignal, self).setUp()
- self.obj = Dummy()
- self.spin = QSpinBox()
- self.spin.setValue(0)
+ def setUp(self):
+ super().setUp()
+ self.obj = Sender()
+ self.spin = QSpinBox()
+ self.spin.setValue(0)
- def tearDown(self):
- super(SpinBoxPySignal, self).tearDown()
- del self.obj
- del self.spin
- # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion
- gc.collect()
+ def tearDown(self):
+ super().tearDown()
+ del self.obj
+ del self.spin
+ # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion
+ gc.collect()
+
+ def testValueChanged(self):
+ """Emission of a python signal to QSpinBox setValue(int)"""
- def testValueChanged(self):
- """Emission of a python signal to QSpinBox setValue(int)"""
- QObject.connect(self.obj, SIGNAL('dummy(int)'), self.spin, SLOT('setValue(int)'))
- self.assertEqual(self.spin.value(), 0)
+ self.obj.foo_int.connect(self.spin.setValue)
+ self.assertEqual(self.spin.value(), 0)
- self.obj.emit(SIGNAL('dummy(int)'), 4)
- self.assertEqual(self.spin.value(), 4)
+ self.obj.foo_int.emit(4)
+ self.assertEqual(self.spin.value(), 4)
- def testValueChangedMultiple(self):
- """Multiple emissions of a python signal to QSpinBox setValue(int)"""
- QObject.connect(self.obj, SIGNAL('dummy(int)'), self.spin, SLOT('setValue(int)'))
- self.assertEqual(self.spin.value(), 0)
+ def testValueChangedMultiple(self):
+ """Multiple emissions of a python signal to QSpinBox setValue(int)"""
+ self.obj.foo_int.connect(self.spin.setValue)
+ self.assertEqual(self.spin.value(), 0)
- self.obj.emit(SIGNAL('dummy(int)'), 4)
- self.assertEqual(self.spin.value(), 4)
+ self.obj.foo_int.emit(4)
+ self.assertEqual(self.spin.value(), 4)
- self.obj.emit(SIGNAL('dummy(int)'), 77)
- self.assertEqual(self.spin.value(), 77)
+ self.obj.foo_int.emit(77)
+ self.assertEqual(self.spin.value(), 77)
-if hasQtGui:
- class WidgetPySignal(UsesQApplication):
- """Tests the connection of python signals to QWidget qt slots."""
+class WidgetPySignal(UsesQApplication):
+ """Tests the connection of python signals to QWidget qt slots."""
+
+ def setUp(self):
+ super(WidgetPySignal, self).setUp()
+ self.obj = Sender()
+ self.widget = QWidget()
- def setUp(self):
- super(WidgetPySignal, self).setUp()
- self.obj = Dummy()
- self.widget = QWidget()
+ def tearDown(self):
+ super(WidgetPySignal, self).tearDown()
+ del self.obj
+ del self.widget
+ # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion
+ gc.collect()
- def tearDown(self):
- super(WidgetPySignal, self).tearDown()
- del self.obj
- del self.widget
- # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion
- gc.collect()
+ def testShow(self):
+ """Emission of a python signal to QWidget slot show()"""
+ self.widget.hide()
- def testShow(self):
- """Emission of a python signal to QWidget slot show()"""
- self.widget.hide()
+ self.obj.foo.connect(self.widget.show)
+ self.assertTrue(not self.widget.isVisible())
- QObject.connect(self.obj, SIGNAL('dummy()'), self.widget, SLOT('show()'))
- self.assertTrue(not self.widget.isVisible())
+ self.obj.foo.emit()
+ self.assertTrue(self.widget.isVisible())
- self.obj.emit(SIGNAL('dummy()'))
- self.assertTrue(self.widget.isVisible())
if __name__ == '__main__':
unittest.main()
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import os
+import sys
+import unittest
+
+from pathlib import Path
+sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
+from init_paths import init_test_paths
+init_test_paths(False)
+
+from PySide6.QtCore import QObject, Signal
+
+
+class Emitter(QObject):
+ sig = Signal(int)
+
+
+class CallableObject(QObject):
+ called = False
+ x = 0
+
+ def __call__(self, x: int):
+ self.called = True
+ self.x = x
+
+
+class QObjectCallableConnectTest(unittest.TestCase):
+ '''Test case for QObject.connect() when the callable is also a QObject.'''
+
+ def testCallableConnect(self):
+ emitter = Emitter()
+ obj = CallableObject()
+ x = 1
+
+ emitter.sig.connect(obj)
+ emitter.sig.emit(x)
+
+ self.assertTrue(obj.called)
+ self.assertEqual(obj.x, x)
+
+
+if __name__ == '__main__':
+ unittest.main()
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, SIGNAL
+from PySide6.QtCore import QObject
class QObjectDestroyed(unittest.TestCase):
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QCoreApplication, QObject, QTimer, SIGNAL
+from PySide6.QtCore import QCoreApplication, QObject, QTimer, Signal
from helper.usesqapplication import UsesQApplication
super().__init__()
+class Sender(QObject):
+ foo = Signal()
+
+
class Receiver(QObject):
def __init__(self):
super().__init__()
'''Test case for QObject.sender() method.'''
def testSenderPythonSignal(self):
- sender = QObject()
+ sender = Sender()
recv = Receiver()
- QObject.connect(sender, SIGNAL('foo()'), recv.callback)
- sender.emit(SIGNAL('foo()'))
+ sender.foo.connect(recv.callback)
+ sender.foo.emit()
self.assertEqual(sender, recv.the_sender)
'''Test case for QObject.sender() method, this one tests the equality on the Receiver object.'''
def testSenderPythonSignal(self):
- sender = QObject()
+ sender = Sender()
recv = Receiver()
- QObject.connect(sender, SIGNAL('foo()'), recv.callback)
- sender.emit(SIGNAL('foo()'))
+ sender.foo.connect(recv.callback)
+ sender.foo.emit()
self.assertEqual(sender, recv.the_sender)
if __name__ == '__main__':
unittest.main()
-
-
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QCoreApplication, QTimeLine
+from PySide6.QtCore import QTimeLine
from helper.usesqapplication import UsesQApplication
if __name__ == '__main__':
unittest.main()
-
if __name__ == '__main__':
unittest.main()
-
self.emitter.mySignal.emit()
self.assertEqual(self.counter, 2)
-# def testConnectWithConfigureMethod(self):
-#
-# def slot():
-# self.counter += 1
-#
-# self.emitter.pyqtConfigure(mySignal=slot)
-# self.assertEqual(self.counter, 0)
-# self.emitter.mySignal.emit()
-# self.assertEqual(self.counter, 1)
-
if __name__ == '__main__':
unittest.main()
-
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, QCoreApplication, QTimeLine, Slot
+from PySide6.QtCore import QObject, QTimeLine, Slot
from helper.usesqapplication import UsesQApplication
if __name__ == '__main__':
unittest.main()
-
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, QCoreApplication, QTimeLine, Signal, Slot
+from PySide6.QtCore import QObject, QTimeLine, Signal, Slot
from helper.usesqapplication import UsesQApplication
if __name__ == '__main__':
unittest.main()
-
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, SIGNAL
+from PySide6.QtCore import QObject, Signal
# Description of the problem
# After creating an PyObject that inherits from QObject, connecting it,
# Somehow the underlying QObject also points to the same position.
-# In PyQt4, the connection works fine with the same memory behavior,
-# so it looks like specific to SIP.
+class Sender(QObject):
+
+ bar = Signal(int)
-class Dummy(QObject):
def __init__(self, parent=None):
QObject.__init__(self, parent)
class Joe(QObject):
+
+ bar = Signal(int)
+
def __init__(self, parent=None):
QObject.__init__(self, parent)
def tearDown(self):
try:
del self.args
- except:
+ except: # noqa: E722
pass
# PYSIDE-535: Need to collect garbage in PyPy to trigger deletion
gc.collect()
def testSegfault(self):
"""Regression: Segfault for qobjects in the same memory position."""
- obj = Dummy()
- QObject.connect(obj, SIGNAL('bar(int)'), self.callback)
+ obj = Sender()
+ obj.bar.connect(self.callback)
self.args = (33,)
- obj.emit(SIGNAL('bar(int)'), self.args[0])
+ obj.bar.emit(self.args[0])
self.assertTrue(self.called)
del obj
# PYSIDE-535: Need to collect garbage in PyPy to trigger deletion
gc.collect()
obj = Joe()
- QObject.connect(obj, SIGNAL('bar(int)'), self.callback)
+ obj.bar.connect(self.callback)
self.args = (33,)
- obj.emit(SIGNAL('bar(int)'), self.args[0])
+ obj.bar.emit(self.args[0])
self.assertTrue(self.called)
if __name__ == '__main__':
unittest.main()
-
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, SIGNAL, SLOT
+from PySide6.QtCore import QObject, Slot
from PySide6.QtWidgets import QPushButton, QWidget
from helper.usesqapplication import UsesQApplication
+class Receiver(QObject):
+ def __init__(self, p=None):
+ super().__init__(p)
+ self.triggered = False
+
+ @Slot(bool, int)
+ def default_parameter_slot(self, bool_value, int_value=0):
+ self.triggered = True
+
+
class SelfConnect(UsesQApplication):
def testButtonClickClose(self):
button = QPushButton()
- button.connect(button, SIGNAL('clicked()'), SLOT('close()'))
+ button.clicked.connect(button.close)
button.show()
self.assertTrue(button.isVisible())
def testWindowButtonClickClose(self):
button = QPushButton()
window = QWidget()
- window.connect(button, SIGNAL('clicked()'), SLOT('close()'))
+ button.clicked.connect(window.close)
window.show()
self.assertTrue(window.isVisible())
button.click()
self.assertTrue(not window.isVisible())
+ def testDefaultParameters(self):
+ button = QPushButton()
+ receiver = Receiver(button)
+ button.clicked.connect(receiver.default_parameter_slot)
+ button.clicked.connect(button.close)
+ button.show()
+ button.click()
+ self.assertTrue(receiver.triggered)
+
if __name__ == '__main__':
unittest.main()
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, SIGNAL, SLOT
+from PySide6.QtCore import QObject, Signal
-class Dummy(QObject):
- """Dummy class used in this test."""
+class Sender(QObject):
+ """Sender class used in this test."""
+
+ foo = Signal()
+ foo_int = Signal(int)
+ foo_int_int_string = Signal(int, int, str)
+ foo_int_qobject = Signal(int, QObject)
+
def __init__(self, parent=None):
QObject.__init__(self, parent)
def tearDown(self):
try:
del self.args
- except:
+ except: # noqa: E722
pass
# PYSIDE-535: Need to collect garbage in PyPy to trigger deletion
gc.collect()
def testNoArgs(self):
"""Short circuit signal without arguments"""
- obj1 = Dummy()
- QObject.connect(obj1, SIGNAL('foo()'), self.callback)
+ obj1 = Sender()
+ obj1.foo.connect(self.callback)
self.args = tuple()
- obj1.emit(SIGNAL('foo()'), *self.args)
+ obj1.foo.emit(*self.args)
self.assertTrue(self.called)
def testWithArgs(self):
"""Short circuit signal with integer arguments"""
- obj1 = Dummy()
+ obj1 = Sender()
- QObject.connect(obj1, SIGNAL('foo(int)'), self.callback)
+ obj1.foo_int.connect(self.callback)
self.args = (42,)
- obj1.emit(SIGNAL('foo(int)'), *self.args)
+ obj1.foo_int.emit(*self.args)
self.assertTrue(self.called)
def testMultipleArgs(self):
"""Short circuit signal with multiple arguments"""
- obj1 = Dummy()
+ obj1 = Sender()
- QObject.connect(obj1, SIGNAL('foo(int,int,QString)'), self.callback)
+ obj1.foo_int_int_string.connect(self.callback)
self.args = (42, 33, 'char')
- obj1.emit(SIGNAL('foo(int,int,QString)'), *self.args)
+ obj1.foo_int_int_string.emit(*self.args)
self.assertTrue(self.called)
def testComplexArgs(self):
"""Short circuit signal with complex arguments"""
- obj1 = Dummy()
+ obj1 = Sender()
- QObject.connect(obj1, SIGNAL('foo(int,QObject*)'), self.callback)
+ obj1.foo_int_qobject.connect(self.callback)
self.args = (42, obj1)
- obj1.emit(SIGNAL('foo(int,QObject*)'), *self.args)
+ obj1.foo_int_qobject.emit(*self.args)
self.assertTrue(self.called)
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, SIGNAL
+from PySide6.QtCore import QObject, Signal
+
+
+class Sender(QObject):
+
+ mysignal_int = Signal(int)
+ mysignal_int_int = Signal(int, int)
+ mysignal_string = Signal(str)
+
+
+class Forwarder(Sender):
+
+ forward = Signal()
+ forward_qobject = Signal(QObject)
def cute_slot():
def setUp(self):
# Set up the basic resources needed
- self.sender = QObject()
- self.forwarder = QObject()
+ self.sender = Sender()
+ self.forwarder = Forwarder()
self.args = None
self.called = False
# Delete used resources
try:
del self.sender
- except:
+ except: # noqa: E722
pass
try:
del self.forwarder
- except:
+ except: # noqa: E722
pass
del self.args
# PYSIDE-535: Need to collect garbage in PyPy to trigger deletion
raise TypeError("Invalid arguments")
def testSignalWithoutArguments(self):
- QObject.connect(self.sender, SIGNAL("destroyed()"),
- self.forwarder, SIGNAL("forward()"))
- QObject.connect(self.forwarder, SIGNAL("forward()"),
- self.callback_noargs)
+ self.sender.destroyed.connect(self.forwarder.forward)
+ self.forwarder.forward.connect(self.callback_noargs)
del self.sender
# PYSIDE-535: Need to collect garbage in PyPy to trigger deletion
gc.collect()
self.assertTrue(self.called)
def testSignalWithOnePrimitiveTypeArgument(self):
- QObject.connect(self.sender, SIGNAL("mysignal(int)"),
- self.forwarder, SIGNAL("mysignal(int)"))
- QObject.connect(self.forwarder, SIGNAL("mysignal(int)"),
- self.callback_args)
+ self.sender.mysignal_int.connect(self.forwarder.mysignal_int)
+ self.forwarder.mysignal_int.connect(self.callback_args)
self.args = (19,)
- self.sender.emit(SIGNAL('mysignal(int)'), *self.args)
+ self.sender.mysignal_int.emit(*self.args)
self.assertTrue(self.called)
def testSignalWithMultiplePrimitiveTypeArguments(self):
- QObject.connect(self.sender, SIGNAL("mysignal(int,int)"),
- self.forwarder, SIGNAL("mysignal(int,int)"))
- QObject.connect(self.forwarder, SIGNAL("mysignal(int,int)"),
- self.callback_args)
+ self.sender.mysignal_int_int.connect(self.forwarder.mysignal_int_int)
+ self.forwarder.mysignal_int_int.connect(self.callback_args)
self.args = (23, 29)
- self.sender.emit(SIGNAL('mysignal(int,int)'), *self.args)
+ self.sender.mysignal_int_int.emit(*self.args)
self.assertTrue(self.called)
def testSignalWithOneStringArgument(self):
- QObject.connect(self.sender, SIGNAL("mysignal(QString)"),
- self.forwarder, SIGNAL("mysignal(QString)"))
- QObject.connect(self.forwarder, SIGNAL("mysignal(QString)"),
- self.callback_args)
+ self.sender.mysignal_string.connect(self.forwarder.mysignal_string)
+ self.forwarder.mysignal_string.connect(self.callback_args)
self.args = ('myargument',)
- self.sender.emit(SIGNAL('mysignal(QString)'), *self.args)
+ self.sender.mysignal_string.emit(*self.args)
self.assertTrue(self.called)
def testSignalWithOneQObjectArgument(self):
- QObject.connect(self.sender, SIGNAL('destroyed(QObject*)'),
- self.forwarder, SIGNAL('forward(QObject*)'))
- QObject.connect(self.forwarder, SIGNAL('forward(QObject*)'),
- self.callback_qobject)
+ self.sender.destroyed.connect(self.forwarder.forward_qobject)
+ self.forwarder.forward_qobject.connect(self.callback_qobject)
obj_name = 'sender'
self.sender.setObjectName(obj_name)
if __name__ == '__main__':
unittest.main()
-
-
class AutoConnectionTest(unittest.TestCase):
def testConnection(self):
- app = QApplication([])
+ app = QApplication([]) # noqa: F841
win = MyObject()
btn = QPushButton("click", win)
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, SIGNAL, Qt
+from PySide6.QtCore import QObject, Signal, Qt
-class Dummy(QObject):
+class Sender(QObject):
"""Dummy class used in this test."""
+
+ foo = Signal()
+
def __init__(self, parent=None):
- QObject.__init__(self, parent)
+ super().__init__(parent)
class TestConnectionTypeSupport(unittest.TestCase):
def testNoArgs(self):
"""Connect signal using a Qt.ConnectionType as argument"""
- obj1 = Dummy()
+ obj1 = Sender()
- QObject.connect(obj1, SIGNAL('foo()'), self.callback, Qt.DirectConnection)
+ obj1.foo.connect(self.callback, Qt.DirectConnection)
self.args = tuple()
- obj1.emit(SIGNAL('foo()'), *self.args)
+ obj1.foo.emit(*self.args)
self.assertTrue(self.called)
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, SIGNAL, SLOT
-
-try:
- from PySide6.QtWidgets import QSpinBox, QPushButton
- hasQtGui = True
-except ImportError:
- hasQtGui = False
+from PySide6.QtWidgets import QSpinBox, QPushButton
from helper.basicpyslotcase import BasicPySlotCase
from helper.usesqapplication import UsesQApplication
-if hasQtGui:
- class ButtonPySlot(UsesQApplication, BasicPySlotCase):
- """Tests the connection of python slots to QPushButton signals"""
-
- def testButtonClicked(self):
- """Connection of a python slot to QPushButton.clicked()"""
- button = QPushButton('Mylabel')
- button.clicked.connect(self.cb)
- self.args = tuple()
- button.emit(SIGNAL('clicked(bool)'), False)
- self.assertTrue(self.called)
-
- def testButtonClick(self):
- """Indirect qt signal emission using the QPushButton.click() method """
- button = QPushButton('label')
- button.clicked.connect(self.cb)
- self.args = tuple()
- button.click()
- self.assertTrue(self.called)
-
-
-if hasQtGui:
- class SpinBoxPySlot(UsesQApplication, BasicPySlotCase):
- """Tests the connection of python slots to QSpinBox signals"""
-
- def setUp(self):
- super(SpinBoxPySlot, self).setUp()
- self.spin = QSpinBox()
-
- def tearDown(self):
- del self.spin
- # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion
- gc.collect()
- super(SpinBoxPySlot, self).tearDown()
-
- def testSpinBoxValueChanged(self):
- """Connection of a python slot to QSpinBox.valueChanged(int)"""
- self.spin.valueChanged.connect(self.cb)
- self.args = [3]
- self.spin.emit(SIGNAL('valueChanged(int)'), *self.args)
- self.assertTrue(self.called)
-
- def testSpinBoxValueChangedImplicit(self):
- """Indirect qt signal emission using QSpinBox.setValue(int)"""
- self.spin.valueChanged.connect(self.cb)
- self.args = [42]
- self.spin.setValue(self.args[0])
- self.assertTrue(self.called)
-
- def atestSpinBoxValueChangedFewArgs(self):
- """Emission of signals with fewer arguments than needed"""
- # XXX: PyQt4 crashes on the assertRaises
- self.spin.valueChanged.connect(self.cb)
- self.args = (554,)
- self.assertRaises(TypeError, self.spin.emit, SIGNAL('valueChanged(int)'))
-
-if hasQtGui:
- class QSpinBoxQtSlots(UsesQApplication):
- """Tests the connection to QSpinBox qt slots"""
-
- qapplication = True
-
- def testSetValueIndirect(self):
- """Indirect signal emission: QSpinBox using valueChanged(int)/setValue(int)"""
- spinSend = QSpinBox()
- spinRec = QSpinBox()
-
- spinRec.setValue(5)
-
- spinSend.valueChanged.connect(spinRec.setValue)
- self.assertEqual(spinRec.value(), 5)
- spinSend.setValue(3)
- self.assertEqual(spinRec.value(), 3)
- self.assertEqual(spinSend.value(), 3)
-
- def testSetValue(self):
- """Direct signal emission: QSpinBox using valueChanged(int)/setValue(int)"""
- spinSend = QSpinBox()
- spinRec = QSpinBox()
-
- spinRec.setValue(5)
- spinSend.setValue(42)
-
- spinSend.valueChanged.connect(spinRec.setValue)
- self.assertEqual(spinRec.value(), 5)
- self.assertEqual(spinSend.value(), 42)
- spinSend.emit(SIGNAL('valueChanged(int)'), 3)
-
- self.assertEqual(spinRec.value(), 3)
- # Direct emission shouldn't change the value of the emitter
- self.assertEqual(spinSend.value(), 42)
-
- spinSend.emit(SIGNAL('valueChanged(int)'), 66)
- self.assertEqual(spinRec.value(), 66)
- self.assertEqual(spinSend.value(), 42)
+
+class ButtonPySlot(UsesQApplication, BasicPySlotCase):
+ """Tests the connection of python slots to QPushButton signals"""
+
+ def testButtonClicked(self):
+ """Connection of a python slot to QPushButton.clicked()"""
+ button = QPushButton('Mylabel')
+ button.clicked.connect(self.cb)
+ self.args = tuple()
+ button.clicked.emit()
+ self.assertTrue(self.called)
+
+ def testButtonClick(self):
+ """Indirect qt signal emission using the QPushButton.click() method """
+ button = QPushButton('label')
+ button.clicked.connect(self.cb)
+ self.args = tuple()
+ button.click()
+ self.assertTrue(self.called)
+
+
+class SpinBoxPySlot(UsesQApplication, BasicPySlotCase):
+ """Tests the connection of python slots to QSpinBox signals"""
+
+ def setUp(self):
+ super(SpinBoxPySlot, self).setUp()
+ self.spin = QSpinBox()
+
+ def tearDown(self):
+ del self.spin
+ # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion
+ gc.collect()
+ super(SpinBoxPySlot, self).tearDown()
+
+ def testSpinBoxValueChanged(self):
+ """Connection of a python slot to QSpinBox.valueChanged(int)"""
+ self.spin.valueChanged.connect(self.cb)
+ self.args = [3]
+ self.spin.valueChanged.emit(*self.args)
+ self.assertTrue(self.called)
+
+ def testSpinBoxValueChangedImplicit(self):
+ """Indirect qt signal emission using QSpinBox.setValue(int)"""
+ self.spin.valueChanged.connect(self.cb)
+ self.args = [42]
+ self.spin.setValue(self.args[0])
+ self.assertTrue(self.called)
+
+ def atestSpinBoxValueChangedFewArgs(self):
+ """Emission of signals with fewer arguments than needed"""
+ self.spin.valueChanged.connect(self.cb)
+ self.args = (554,)
+ self.assertRaises(TypeError, self.spin.valueChanged.emit)
+
+
+class QSpinBoxQtSlots(UsesQApplication):
+ """Tests the connection to QSpinBox qt slots"""
+
+ qapplication = True
+
+ def testSetValueIndirect(self):
+ """Indirect signal emission: QSpinBox using valueChanged(int)/setValue(int)"""
+ spinSend = QSpinBox()
+ spinRec = QSpinBox()
+
+ spinRec.setValue(5)
+
+ spinSend.valueChanged.connect(spinRec.setValue)
+ self.assertEqual(spinRec.value(), 5)
+ spinSend.setValue(3)
+ self.assertEqual(spinRec.value(), 3)
+ self.assertEqual(spinSend.value(), 3)
+
+ def testSetValue(self):
+ """Direct signal emission: QSpinBox using valueChanged(int)/setValue(int)"""
+ spinSend = QSpinBox()
+ spinRec = QSpinBox()
+
+ spinRec.setValue(5)
+ spinSend.setValue(42)
+
+ spinSend.valueChanged.connect(spinRec.setValue)
+ self.assertEqual(spinRec.value(), 5)
+ self.assertEqual(spinSend.value(), 42)
+ spinSend.valueChanged.emit(3)
+
+ self.assertEqual(spinRec.value(), 3)
+ # Direct emission shouldn't change the value of the emitter
+ self.assertEqual(spinSend.value(), 42)
+
+ spinSend.valueChanged.emit(66)
+ self.assertEqual(spinRec.value(), 66)
+ self.assertEqual(spinSend.value(), 42)
if __name__ == '__main__':
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, SIGNAL, SLOT, QProcess, QTimeLine
+from PySide6.QtCore import QObject, Signal, SIGNAL, QProcess, QTimeLine
-from helper.basicpyslotcase import BasicPySlotCase
from helper.usesqapplication import UsesQApplication
def testArgsToNoArgsSignal(self):
'''Passing arguments to a signal without arguments'''
process = QProcess()
- self.assertRaises(TypeError, process.emit, SIGNAL('started()'), 42)
+ self.assertRaises(TypeError, process.started.emit, 42)
class MoreArgsOnEmit(UsesQApplication):
def testMoreArgs(self):
'''Passing more arguments than needed'''
process = QProcess()
- self.assertRaises(TypeError, process.emit, SIGNAL('finished(int)'), 55, 55)
+ self.assertRaises(TypeError, process.finished.emit, 55, QProcess.ExitStatus.NormalExit, 42)
-class Dummy(QObject):
- '''Dummy class'''
- pass
+class Sender(QObject):
+ '''Sender class'''
+
+ dummy = Signal()
+ dummy_int = Signal(int)
class PythonSignalToCppSlots(UsesQApplication):
def testWithoutArgs(self):
'''Connect python signal to QTimeLine.toggleDirection()'''
timeline = QTimeLine()
- dummy = Dummy()
- QObject.connect(dummy, SIGNAL('dummy()'),
- timeline, SLOT('toggleDirection()'))
+ sender = Sender()
+ sender.dummy.connect(timeline.toggleDirection)
orig_dir = timeline.direction()
- dummy.emit(SIGNAL('dummy()'))
+ sender.dummy.emit()
new_dir = timeline.direction()
if orig_dir == QTimeLine.Forward:
def testWithArgs(self):
'''Connect python signals to QTimeLine.setCurrentTime(int)'''
timeline = QTimeLine()
- dummy = Dummy()
+ sender = Sender()
- QObject.connect(dummy, SIGNAL('dummy(int)'),
- timeline, SLOT('setCurrentTime(int)'))
+ sender.dummy_int.connect(timeline.setCurrentTime)
current = timeline.currentTime()
- dummy.emit(SIGNAL('dummy(int)'), current + 42)
+ sender.dummy_int.emit(current + 42)
self.assertEqual(timeline.currentTime(), current + 42)
process = QProcess()
timeline = QTimeLine()
- QObject.connect(process, SIGNAL('finished(int, QProcess::ExitStatus)'),
- timeline, SLOT('toggleDirection()'))
+ process.finished.connect(timeline.toggleDirection)
orig_dir = timeline.direction()
process.start(sys.executable, ['-c', '"print 42"'])
- process.waitForFinished()
+ self.assertTrue(process.waitForStarted())
+ self.assertTrue(process.waitForFinished())
new_dir = timeline.direction()
def testIt(self):
global called
called = False
- o = QObject()
- o.connect(o, SIGNAL("ASignal()"), functools.partial(someSlot, "partial .."))
- o.emit(SIGNAL("ASignal()"))
+ o = Sender()
+ o.dummy.connect(functools.partial(someSlot, "partial .."))
+ o.dummy.emit()
self.assertTrue(called)
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, SIGNAL
+from PySide6.QtCore import QObject
class SignalManagerRefCount(unittest.TestCase):
- """Simple test case to check if the signal_manager is erroneously incrementing the object refcounter"""
+ """Simple test case to check if the signal_manager is erroneously incrementing the
+ object refcounter."""
@unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount")
def testObjectRefcount(self):
refcount = sys.getrefcount(obj)
obj.destroyed.connect(callback)
self.assertEqual(refcount, sys.getrefcount(obj))
- QObject.disconnect(obj, SIGNAL('destroyed()'), callback)
+ obj.destroyed.disconnect(callback)
self.assertEqual(refcount, sys.getrefcount(obj))
if __name__ == '__main__':
unittest.main()
-
name = "Old"
+class Sender(QObject):
+ dummySignal = Signal()
+
+
class Obj(QObject):
dummySignalArgs = Signal(str)
numberSignal = Signal(int)
def testStaticSlot(self):
global called
- sender = Obj()
- sender.connect(sender, SIGNAL("dummySignal()"), Obj.static_method)
- sender.emit(SIGNAL("dummySignal()"))
+ sender = Sender()
+ sender.dummySignal.connect(Obj.static_method)
+ sender.dummySignal.emit()
self.assertTrue(called)
def testStaticSlotArgs(self):
if __name__ == '__main__':
unittest.main()
-
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QCoreApplication, QObject, QTimeLine, SIGNAL
+from PySide6.QtCore import QCoreApplication, QTimeLine
class SignalPrimitiveTypeTest(unittest.TestCase):
if __name__ == '__main__':
unittest.main()
-
-
--- /dev/null
+{
+ "files": ["anonymous_slot_leak_test.py", "args_dont_match_test.py",
+ "bug_189.py", "bug_311.py", "bug_312.py", "bug_319.py", "bug_79.py",
+ "decorators_test.py", "disconnect_test.py", "invalid_callback_test.py",
+ "lambda_gui_test.py", "lambda_test.py", "leaking_signal_test.py",
+ "multiple_connections_gui_test.py", "multiple_connections_test.py",
+ "pysignal_test.py", "qobject_callable_connect_test.py", "qobject_destroyed_test.py",
+ "qobject_receivers_test.py", "qobject_sender_test.py", "ref01_test.py",
+ "ref02_test.py", "ref03_test.py", "ref04_test.py", "ref05_test.py",
+ "ref06_test.py", "segfault_proxyparent_test.py",
+ "self_connect_test.py", "short_circuit_test.py",
+ "signal2signal_connect_test.py", "signal_across_threads.py",
+ "signal_autoconnect_test.py", "signal_connectiontype_support_test.py",
+ "signal_emission_gui_test.py", "signal_emission_test.py",
+ "signal_enum_test.py", "signal_func_test.py", "signal_manager_refcount_test.py",
+ "signal_newenum_test.py", "signal_number_limit_test.py",
+ "signal_object_test.py", "signal_signature_test.py", "signal_with_primitive_type_test.py",
+ "slot_reference_count_test.py", "static_metaobject_test.py"]
+}
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, SIGNAL, SLOT
+from PySide6.QtCore import QObject, Signal
class Dummy(QObject):
+ foo = Signal()
+
def dispatch(self):
- self.emit(SIGNAL('foo()'))
+ self.foo.emit()
class PythonSignalRefCount(unittest.TestCase):
self.assertEqual(sys.getrefcount(cb), 2)
- QObject.connect(self.emitter, SIGNAL('foo()'), cb)
+ self.emitter.foo.connect(cb)
self.assertEqual(sys.getrefcount(cb), 3)
- QObject.disconnect(self.emitter, SIGNAL('foo()'), cb)
+ self.emitter.foo.disconnect(cb)
self.assertEqual(sys.getrefcount(cb), 2)
self.emitter.destroyed.connect(cb)
self.assertEqual(sys.getrefcount(cb), 3)
- QObject.disconnect(self.emitter, SIGNAL('destroyed()'), cb)
+ self.emitter.destroyed.disconnect(cb)
self.assertEqual(sys.getrefcount(cb), 2)
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, SIGNAL, Slot
+from PySide6.QtCore import QObject, Signal, Slot, SIGNAL
from helper.usesqapplication import UsesQApplication
+class Sender(QObject):
+
+ foo = Signal()
+ foo2 = Signal()
+
+
class MyObject(QObject):
+
+ foo2 = Signal()
+
def __init__(self, parent=None):
- QObject.__init__(self, parent)
+ super().__init__(parent)
self._slotCalledCount = 0
# this '@Slot()' is needed to get the right sort order in testSharedSignalEmission.
class StaticMetaObjectTest(UsesQApplication):
def testSignalPropagation(self):
- o = MyObject()
+ """Old style, dynamic signal creation."""
+ o = QObject()
o2 = MyObject()
# SIGNAL foo not created yet
self.assertEqual(o.metaObject().indexOfSignal("foo()"), -1)
def testSharedSignalEmission(self):
- o = QObject()
+ o = Sender()
m = MyObject()
- o.connect(SIGNAL("foo2()"), m.mySlot)
- m.connect(SIGNAL("foo2()"), m.mySlot)
- o.emit(SIGNAL("foo2()"))
+ o.foo2.connect(m.mySlot)
+ m.foo2.connect(m.mySlot)
+ o.foo2.emit()
self.assertEqual(m._slotCalledCount, 1)
del o
# PYSIDE-535: Need to collect garbage in PyPy to trigger deletion
gc.collect()
- m.emit(SIGNAL("foo2()"))
+ m.foo2.emit()
self.assertEqual(m._slotCalledCount, 2)
from unittest.mock import patch
sys.path.append(os.fspath(Path(__file__).resolve().parents[2]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths # noqa: E402
init_test_paths(False)
self.config_file = self.temp_example / "pysidedeploy.spec"
self.buildozer_config = self.temp_example / "buildozer.spec"
- def test_dry_run(self, mock_extract_jar):
+ @patch("deploy_lib.android.android_config.AndroidConfig._find_local_libs")
+ @patch("deploy_lib.android.android_config.AndroidConfig._find_dependent_qt_modules")
+ @patch("deploy_lib.android.android_config.find_qtlibs_in_wheel")
+ def test_dry_run(self, mock_qtlibs, mock_extraqtmodules, mock_local_libs, mock_extract_jar):
+ mock_qtlibs.return_value = self.pyside_wheel / "PySide6/Qt/lib"
+ mock_extraqtmodules.return_value = []
+ dependent_plugins = ["platforms_qtforandroid",
+ "platforminputcontexts_qtvirtualkeyboardplugin",
+ "iconengines_qsvgicon"]
+ mock_local_libs.return_value = [], dependent_plugins
self.android_deploy.main(name="android_app", shiboken_wheel=self.shiboken_wheel,
pyside_wheel=self.pyside_wheel, ndk_path=self.ndk_path,
dry_run=True, force=True)
+
self.assertEqual(mock_extract_jar.call_count, 0)
+ self.assertEqual(mock_qtlibs.call_count, 1)
+ self.assertEqual(mock_extraqtmodules.call_count, 1)
+ self.assertEqual(mock_local_libs.call_count, 1)
@patch("deploy_lib.android.buildozer.BuildozerConfig._BuildozerConfig__find_jars")
@patch("deploy_lib.android.android_config.AndroidConfig.recipes_exist")
- @patch("deploy_lib.android.buildozer.BuildozerConfig."
- "_BuildozerConfig__find_dependent_qt_modules")
- @patch("deploy_lib.android.buildozer.find_qtlibs_in_wheel")
+ @patch("deploy_lib.android.android_config.AndroidConfig._find_dependent_qt_modules")
+ @patch("deploy_lib.android.android_config.find_qtlibs_in_wheel")
def test_config(self, mock_qtlibs, mock_extraqtmodules, mock_recipes_exist, mock_find_jars,
mock_extract_jar):
jar_dir = "tmp/jar/PySide6/jar"
self.assertIn(str(self.ndk_path), config_obj.get_value("buildozer", "ndk_path"))
self.assertEqual(config_obj.get_value("buildozer", "sdk_path"), '')
expected_modules = {"Core", "Gui"}
- obtained_modules = set(config_obj.get_value("buildozer", "modules").split(","))
+ obtained_modules = set(config_obj.get_value("qt", "modules").split(","))
self.assertEqual(obtained_modules, expected_modules)
- expected_local_libs = "plugins_platforms_qtforandroid"
+ expected_local_libs = ""
self.assertEqual(config_obj.get_value("buildozer", "local_libs"),
expected_local_libs)
self.assertEqual(config_obj.get_value("buildozer", "arch"), "x86_64")
(self.temp_qml_example / "stringlistmodel.py").rename(self.temp_qml_example / "main.py")
(self.temp_qml_example / "stringlistmodel.pyproject").unlink()
- @patch("deploy_lib.android.buildozer.BuildozerConfig._BuildozerConfig__find_local_libs")
+ @patch("deploy_lib.android.android_config.AndroidConfig._find_local_libs")
@patch("deploy_lib.android.buildozer.BuildozerConfig._BuildozerConfig__find_jars")
@patch("deploy_lib.android.android_config.AndroidConfig.recipes_exist")
- @patch("deploy_lib.android.buildozer.BuildozerConfig."
- "_BuildozerConfig__find_dependent_qt_modules")
- @patch("deploy_lib.android.buildozer.find_qtlibs_in_wheel")
+ @patch("deploy_lib.android.android_config.AndroidConfig._find_dependent_qt_modules")
+ @patch("deploy_lib.android.android_config.find_qtlibs_in_wheel")
def test_config_with_Qml(self, mock_qtlibs, mock_extraqtmodules, mock_recipes_exist,
mock_find_jars, mock_local_libs, mock_extract_jar,
mock_qmlimportscanner):
self.assertTrue(self.config_file.exists())
self.assertTrue(self.buildozer_config_file.exists())
- # test config file contents
config_obj = self.deploy_lib.BaseConfig(config_file=self.config_file)
expected_modules = {"Quick", "Core", "Gui", "Network", "Qml", "QmlModels", "OpenGL"}
- obtained_modules = set(config_obj.get_value("buildozer", "modules").split(","))
+ obtained_modules = set(config_obj.get_value("qt", "modules").split(","))
self.assertEqual(obtained_modules, expected_modules)
- expected_local_libs = "plugins_platforms_qtforandroid"
+ expected_local_libs = ""
self.assertEqual(config_obj.get_value("buildozer", "local_libs"),
expected_local_libs)
expected_qt_plugins = set(dependent_plugins)
import sys
import os
import importlib
+import platform
from pathlib import Path
from unittest.mock import patch
from unittest import mock
+sys.path.append(os.fspath(Path(__file__).resolve().parents[2]))
+from init_paths import init_test_paths, _get_qt_lib_dir # noqa: E402
+init_test_paths(False)
+
def is_pyenv_python():
pyenv_root = os.environ.get("PYENV_ROOT")
cls.deploy_lib = importlib.import_module("deploy_lib")
cls.deploy = importlib.import_module("deploy")
sys.modules["deploy"] = cls.deploy
+ files_to_ignore = [".cpp.o", ".qsb", ".webp"]
+ cls.dlls_ignore_nuitka = " ".join([f"--noinclude-dlls=*{file}"
+ for file in files_to_ignore])
# required for comparing long strings
cls.maxDiff = None
os.chdir(self.current_dir)
+@unittest.skipIf(sys.platform == "darwin" and int(platform.mac_ver()[0].split('.')[0]) <= 11,
+ "Test only works on macOS version 12+")
+@patch("deploy_lib.config.QtDependencyReader.find_plugin_dependencies")
class TestPySide6DeployWidgets(DeployTestBase):
@classmethod
def setUpClass(cls):
os.chdir(self.temp_example_widgets)
self.main_file = self.temp_example_widgets / "tetrix.py"
self.deployment_files = self.temp_example_widgets / "deployment"
+ # All the plugins included. This is different from plugins_nuitka, because Nuitka bundles
+ # some plugins by default
+ self.all_plugins = ["accessiblebridge", "egldeviceintegrations", "generic", "iconengines",
+ "imageformats", "platforminputcontexts", "platforms",
+ "platforms/darwin", "platformthemes", "styles", "xcbglintegrations"]
+ # Plugins that needs to be passed to Nuitka
+ plugins_nuitka = ("accessiblebridge,platforminputcontexts,platforms/darwin")
self.expected_run_cmd = (
- f"{sys.executable} -m nuitka {str(self.main_file)} --follow-imports --onefile"
+ f"{sys.executable} -m nuitka {str(self.main_file)} --follow-imports"
f" --enable-plugin=pyside6 --output-dir={str(self.deployment_files)} --quiet"
f" --noinclude-qt-translations"
+ f" --include-qt-plugins={plugins_nuitka}"
+ f" {self.dlls_ignore_nuitka}"
)
if sys.platform.startswith("linux"):
- self.expected_run_cmd += f" --linux-icon={str(self.linux_icon)}"
+ self.expected_run_cmd += f" --linux-icon={str(self.linux_icon)} --onefile"
elif sys.platform == "darwin":
- self.expected_run_cmd += f" --macos-app-icon={str(self.macos_icon)}"
+ self.expected_run_cmd += (f" --macos-app-icon={str(self.macos_icon)}"
+ " --macos-create-app-bundle --standalone")
elif sys.platform == "win32":
- self.expected_run_cmd += f" --windows-icon-from-ico={str(self.win_icon)}"
+ self.expected_run_cmd += f" --windows-icon-from-ico={str(self.win_icon)} --onefile"
if is_pyenv_python():
self.expected_run_cmd += " --static-libpython=no"
self.config_file = self.temp_example_widgets / "pysidedeploy.spec"
- def testWidgetDryRun(self):
+ def testWidgetDryRun(self, mock_plugins):
+ mock_plugins.return_value = self.all_plugins
# Checking for dry run commands is equivalent to mocking the
# subprocess.check_call() in commands.py as the the dry run command
# is the command being run.
original_output = self.deploy.main(self.main_file, dry_run=True, force=True)
self.assertEqual(original_output, self.expected_run_cmd)
- def testWidgetConfigFile(self):
+ @patch("deploy_lib.dependency_util.QtDependencyReader.get_qt_libs_dir")
+ def testWidgetConfigFile(self, mock_sitepackages, mock_plugins):
+ mock_sitepackages.return_value = Path(_get_qt_lib_dir())
+ mock_plugins.return_value = self.all_plugins
# includes both dry run and config_file tests
-
# init
init_result = self.deploy.main(self.main_file, init=True, force=True)
self.assertEqual(init_result, None)
self.assertEqual(config_obj.get_value("app", "project_dir"), ".")
self.assertEqual(config_obj.get_value("app", "exec_directory"), ".")
self.assertEqual(config_obj.get_value("python", "packages"),
- "nuitka==1.8.0,ordered_set,zstandard")
+ "Nuitka==2.3.2")
self.assertEqual(config_obj.get_value("qt", "qml_files"), "")
equ_base = "--quiet --noinclude-qt-translations"
equ_value = equ_base + " --static-libpython=no" if is_pyenv_python() else equ_base
self.assertEqual(config_obj.get_value("nuitka", "extra_args"), equ_value)
self.assertEqual(config_obj.get_value("qt", "excluded_qml_plugins"), "")
+ expected_modules = {"Core", "Gui", "Widgets"}
+ if sys.platform != "win32":
+ expected_modules.add("DBus")
+ obtained_modules = set(config_obj.get_value("qt", "modules").split(","))
+ self.assertEqual(obtained_modules, expected_modules)
+ obtained_qt_plugins = config_obj.get_value("qt", "plugins").split(",")
+ self.assertEqual(obtained_qt_plugins.sort(), self.all_plugins.sort())
self.config_file.unlink()
- def testErrorReturns(self):
+ def testErrorReturns(self, mock_plugins):
+ mock_plugins.return_value = self.all_plugins
# main file and config file does not exists
fake_main_file = self.main_file.parent / "main.py"
with self.assertRaises(RuntimeError) as context:
self.assertTrue("Directory does not contain main.py file." in str(context.exception))
+@unittest.skipIf(sys.platform == "darwin" and int(platform.mac_ver()[0].split('.')[0]) <= 11,
+ "Test only works on macOS version 12+")
+@patch("deploy_lib.config.QtDependencyReader.find_plugin_dependencies")
class TestPySide6DeployQml(DeployTestBase):
@classmethod
def setUpClass(cls):
self.deployment_files = self.temp_example_qml / "deployment"
self.first_qml_file = "main.qml"
self.second_qml_file = "MovingRectangle.qml"
+
+ # All the plugins included. This is different from plugins_nuitka, because Nuitka bundles
+ # some plugins by default
+ self.all_plugins = ["accessiblebridge", "egldeviceintegrations", "generic", "iconengines",
+ "imageformats", "networkaccess", "networkinformation",
+ "platforminputcontexts", "platforms", "platforms/darwin",
+ "platformthemes", "qmltooling", "scenegraph", "tls",
+ "xcbglintegrations"]
+ # Plugins that needs to be passed to Nuitka
+ plugins_nuitka = ("accessiblebridge,networkaccess,networkinformation,platforminputcontexts,"
+ "platforms/darwin,qml,qmltooling,scenegraph")
self.expected_run_cmd = (
- f"{sys.executable} -m nuitka {str(self.main_file)} --follow-imports --onefile"
+ f"{sys.executable} -m nuitka {str(self.main_file)} --follow-imports"
f" --enable-plugin=pyside6 --output-dir={str(self.deployment_files)} --quiet"
- f" --noinclude-qt-translations --include-qt-plugins=all"
+ f" --noinclude-qt-translations"
+ f" {self.dlls_ignore_nuitka}"
+ " --noinclude-dlls=*/qml/QtQuickEffectMaker/*"
+ f" --include-qt-plugins={plugins_nuitka}"
f" --include-data-files={str(self.temp_example_qml / self.first_qml_file)}="
f"./main.qml --include-data-files="
- f"{str(self.temp_example_qml /self.second_qml_file)}=./MovingRectangle.qml"
+ f"{str(self.temp_example_qml / self.second_qml_file)}=./MovingRectangle.qml"
)
if sys.platform != "win32":
)
if sys.platform.startswith("linux"):
- self.expected_run_cmd += f" --linux-icon={str(self.linux_icon)}"
+ self.expected_run_cmd += f" --linux-icon={str(self.linux_icon)} --onefile"
elif sys.platform == "darwin":
- self.expected_run_cmd += f" --macos-app-icon={str(self.macos_icon)}"
+ self.expected_run_cmd += (f" --macos-app-icon={str(self.macos_icon)}"
+ " --macos-create-app-bundle --standalone")
elif sys.platform == "win32":
- self.expected_run_cmd += f" --windows-icon-from-ico={str(self.win_icon)}"
+ self.expected_run_cmd += f" --windows-icon-from-ico={str(self.win_icon)} --onefile"
if is_pyenv_python():
self.expected_run_cmd += " --static-libpython=no"
self.config_file = self.temp_example_qml / "pysidedeploy.spec"
- def testQmlConfigFile(self):
+ @patch("deploy_lib.dependency_util.QtDependencyReader.get_qt_libs_dir")
+ def testQmlConfigFile(self, mock_sitepackages, mock_plugins):
+ mock_sitepackages.return_value = Path(_get_qt_lib_dir())
+ mock_plugins.return_value = self.all_plugins
# create config file
with patch("deploy_lib.config.run_qmlimportscanner") as mock_qmlimportscanner:
mock_qmlimportscanner.return_value = ["QtQuick"]
self.assertEqual(config_obj.get_value("app", "project_dir"), ".")
self.assertEqual(config_obj.get_value("app", "exec_directory"), ".")
self.assertEqual(config_obj.get_value("python", "packages"),
- "nuitka==1.8.0,ordered_set,zstandard")
+ "Nuitka==2.3.2")
self.assertEqual(config_obj.get_value("qt", "qml_files"), "main.qml,MovingRectangle.qml")
equ_base = "--quiet --noinclude-qt-translations"
equ_value = equ_base + " --static-libpython=no" if is_pyenv_python() else equ_base
config_obj.get_value("qt", "excluded_qml_plugins"),
"QtCharts,QtQuick3D,QtSensors,QtTest,QtWebEngine",
)
+ expected_modules = {"Core", "Gui", "Qml", "Quick", "Network", "OpenGL", "QmlModels"}
+ if sys.platform != "win32":
+ expected_modules.add("DBus")
+ obtained_modules = set(config_obj.get_value("qt", "modules").split(","))
+ self.assertEqual(obtained_modules, expected_modules)
+ obtained_qt_plugins = config_obj.get_value("qt", "plugins").split(",")
+ self.assertEqual(obtained_qt_plugins.sort(), self.all_plugins.sort())
self.config_file.unlink()
- def testQmlDryRun(self):
+ def testQmlDryRun(self, mock_plugins):
+ mock_plugins.return_value = self.all_plugins
with patch("deploy_lib.config.run_qmlimportscanner") as mock_qmlimportscanner:
mock_qmlimportscanner.return_value = ["QtQuick"]
original_output = self.deploy.main(self.main_file, dry_run=True, force=True)
self.assertEqual(original_output, self.expected_run_cmd)
self.assertEqual(mock_qmlimportscanner.call_count, 1)
- def testMainFileDryRun(self):
+ def testMainFileDryRun(self, mock_plugins):
+ mock_plugins.return_value = self.all_plugins
with patch("deploy_lib.config.run_qmlimportscanner") as mock_qmlimportscanner:
mock_qmlimportscanner.return_value = ["QtQuick"]
original_output = self.deploy.main(Path.cwd() / "main.py", dry_run=True, force=True)
self.assertEqual(mock_qmlimportscanner.call_count, 1)
+@unittest.skipIf(sys.platform == "darwin" and int(platform.mac_ver()[0].split('.')[0]) <= 11,
+ "Test only works on macOS version 12+")
class TestPySide6DeployWebEngine(DeployTestBase):
@classmethod
def setUpClass(cls):
shutil.copytree(example_webenginequick, Path(cls.temp_dir) / "nanobrowser")
).resolve()
- # this test case retains the QtWebEngine dlls
- def testWebEngineQuickDryRun(self):
+ @patch("deploy_lib.config.QtDependencyReader.find_plugin_dependencies")
+ @patch("deploy_lib.dependency_util.QtDependencyReader.get_qt_libs_dir")
+ def testWebEngineQuickDryRun(self, mock_sitepackages, mock_plugins):
+ mock_sitepackages.return_value = Path(_get_qt_lib_dir())
+ all_plugins = ["accessiblebridge", "egldeviceintegrations", "generic", "iconengines",
+ "imageformats", "networkaccess", "networkinformation",
+ "platforminputcontexts", "platforms", "platforms/darwin",
+ "platformthemes", "qmltooling", "scenegraph", "tls",
+ "xcbglintegrations"]
+ mock_plugins.return_value = all_plugins
+ # this test case retains the QtWebEngine dlls
# setup
os.chdir(self.temp_example_webenginequick)
main_file = self.temp_example_webenginequick / "quicknanobrowser.py"
deployment_files = self.temp_example_webenginequick / "deployment"
+ # Plugins that needs to be passed to Nuitka
+ plugins_nuitka = ("accessiblebridge,networkaccess,networkinformation,platforminputcontexts,"
+ "platforms/darwin,qml,qmltooling,scenegraph")
qml_files = [
"ApplicationRoot.qml",
"BrowserDialog.qml",
]
data_files_cmd = " ".join(
[
- f"--include-data-files={str(self.temp_example_webenginequick/file)}=./{file}"
+ f"--include-data-files={str(self.temp_example_webenginequick / file)}=./{file}"
for file in qml_files
]
)
expected_run_cmd = (
- f"{sys.executable} -m nuitka {str(main_file)} --follow-imports --onefile"
+ f"{sys.executable} -m nuitka {str(main_file)} --follow-imports"
f" --enable-plugin=pyside6 --output-dir={str(deployment_files)} --quiet"
f" --noinclude-qt-translations --include-qt-plugins=all"
f" {data_files_cmd}"
+ f" --include-qt-plugins={plugins_nuitka}"
+ f" {self.dlls_ignore_nuitka}"
+ " --noinclude-dlls=*/qml/QtQuickEffectMaker/*"
)
if sys.platform != "win32":
)
if sys.platform.startswith("linux"):
- expected_run_cmd += f" --linux-icon={str(self.linux_icon)}"
+ expected_run_cmd += f" --linux-icon={str(self.linux_icon)} --onefile"
elif sys.platform == "darwin":
- expected_run_cmd += f" --macos-app-icon={str(self.macos_icon)}"
+ expected_run_cmd += (f" --macos-app-icon={str(self.macos_icon)}"
+ " --macos-create-app-bundle --standalone")
elif sys.platform == "win32":
- expected_run_cmd += f" --windows-icon-from-ico={str(self.win_icon)}"
+ expected_run_cmd += f" --windows-icon-from-ico={str(self.win_icon)} --onefile"
config_file = self.temp_example_webenginequick / "pysidedeploy.spec"
config_obj.get_value("qt", "excluded_qml_plugins"),
"QtCharts,QtQuick3D,QtSensors,QtTest",
)
+ expected_modules = {"Core", "Gui", "Quick", "Qml", "WebEngineQuick", "Network", "OpenGL",
+ "Positioning", "WebEngineCore", "WebChannel", "WebChannelQuick",
+ "QmlModels"}
+ if sys.platform != "win32":
+ expected_modules.add("DBus")
+ obtained_modules = set(config_obj.get_value("qt", "modules").split(","))
+ self.assertEqual(obtained_modules, expected_modules)
if __name__ == "__main__":
set(shiboken_MAJOR_VERSION "6")
-set(shiboken_MINOR_VERSION "6")
+set(shiboken_MINOR_VERSION "7")
set(shiboken_MICRO_VERSION "2")
set(shiboken_PRE_RELEASE_VERSION_TYPE "")
set(shiboken_PRE_RELEASE_VERSION "")
abstractmetalang.cpp abstractmetalang.h abstractmetalang_helpers.h abstractmetalang_typedefs.h
abstractmetatype.cpp abstractmetatype.h
addedfunction.cpp addedfunction.h addedfunction_p.h
+anystringview_helpers.cpp anystringview_helpers.h
apiextractor.cpp apiextractor.h apiextractorflags.h
apiextractorresult.cpp apiextractorresult.h
arraytypeentry.h
return d->m_enums;
}
+const QMultiHash<QString, QString> &AbstractMetaBuilder::typedefTargetToName() const
+{
+ return d->m_typedefTargetToName;
+}
+
void AbstractMetaBuilderPrivate::checkFunctionModifications() const
{
const auto &entries = TypeDatabase::instance()->entries();
void AbstractMetaBuilderPrivate::registerToStringCapabilityIn(const NamespaceModelItem &nsItem)
{
- const FunctionList &streamOps = nsItem->findFunctions(u"operator<<"_s);
+ const FunctionList &streamOps = nsItem->findFunctions("operator<<");
for (const FunctionModelItem &item : streamOps)
registerToStringCapability(item, nullptr);
for (const NamespaceModelItem &ni : nsItem->namespaces())
streamFunction->setArguments(arguments);
- *streamFunction += AbstractMetaFunction::FinalInTargetLang;
streamFunction->setAccess(Access::Public);
AbstractMetaClassPtr funcClass;
// this is a temporary solution before new type revision implementation
// We need move QMetaObject register before QObject.
Dependencies additionalDependencies;
- if (auto qObjectClass = AbstractMetaClass::findClass(m_metaClasses, u"QObject")) {
- if (auto qMetaObjectClass = AbstractMetaClass::findClass(m_metaClasses, u"QMetaObject")) {
+ if (auto qObjectClass = AbstractMetaClass::findClass(m_metaClasses, "QObject")) {
+ if (auto qMetaObjectClass = AbstractMetaClass::findClass(m_metaClasses, "QMetaObject")) {
Dependency dependency;
dependency.parent = qMetaObjectClass;
dependency.child = qObjectClass;
}
{
- const FunctionList &hashFunctions = dom->findFunctions(u"qHash"_s);
+ const FunctionList &hashFunctions = dom->findFunctions("qHash");
for (const FunctionModelItem &item : hashFunctions)
registerHashFunction(item, nullptr);
}
return metaEnum;
}
-AbstractMetaClassPtr AbstractMetaBuilderPrivate::traverseTypeDef(const FileModelItem &,
- const TypeDefModelItem &typeDef,
- const AbstractMetaClassPtr ¤tClass)
+AbstractMetaClassPtr
+ AbstractMetaBuilderPrivate::traverseTypeDef(const FileModelItem &dom,
+ const TypeDefModelItem &typeDef,
+ const AbstractMetaClassPtr ¤tClass)
+{
+ auto result = traverseTypeDefHelper(dom, typeDef, currentClass);
+ if (!result && typeDef->type().isPlain()) {
+ const auto &type = typeDef->type();
+ QString fullName;
+ if (currentClass)
+ fullName += currentClass->qualifiedCppName() + "::"_L1;
+ fullName += typeDef->name();
+ QString targetName = typeDef->type().toString();
+ m_typedefTargetToName.insert(targetName, fullName);
+ const QByteArray normalized = QMetaObject::normalizedType(targetName.toUtf8().constData());
+ if (targetName != QLatin1StringView(normalized))
+ m_typedefTargetToName.insert(QString::fromUtf8(normalized), fullName);
+ }
+ return result;
+}
+
+AbstractMetaClassPtr
+ AbstractMetaBuilderPrivate::traverseTypeDefHelper(const FileModelItem &,
+ const TypeDefModelItem &typeDef,
+ const AbstractMetaClassPtr ¤tClass)
{
TypeDatabase *types = TypeDatabase::instance();
QString className = stripTemplateArgs(typeDef->name());
static inline QString fieldSignatureWithType(const VariableModelItem &field)
{
- return field->name() + QStringLiteral(" -> ") + field->type().toString();
+ return field->name() + " -> "_L1 + field->type().toString();
}
static inline QString qualifiedFieldSignatureWithType(const QString &className,
return;
TypeDatabase *types = TypeDatabase::instance();
- static const QRegularExpression operatorRegExp(QStringLiteral("^operator "));
+ static const QRegularExpression operatorRegExp("^operator "_L1);
Q_ASSERT(operatorRegExp.isValid());
QString castTo = metaFunction->name().remove(operatorRegExp).trimmed();
for (AbstractMetaFunction *metaFunction : functions) {
if (metaClass->isNamespace())
- *metaFunction += AbstractMetaFunction::Static;
+ metaFunction->setCppAttribute(FunctionAttribute::Static);
const auto propertyFunction = metaClass->searchPropertyFunction(metaFunction->name());
if (propertyFunction.index >= 0) {
}
}
- const bool isInvalidDestructor = metaFunction->isDestructor() && metaFunction->isPrivate();
- const bool isInvalidConstructor = metaFunction->functionType() == AbstractMetaFunction::ConstructorFunction
- && metaFunction->isPrivate();
- if (isInvalidConstructor)
+ if (metaFunction->functionType() == AbstractMetaFunction::ConstructorFunction
+ && metaFunction->isPrivate()) {
metaClass->setHasPrivateConstructor(true);
- if ((isInvalidDestructor || isInvalidConstructor)
- && !metaClass->hasNonPrivateConstructor()) {
- *metaClass += AbstractMetaClass::FinalInTargetLang;
- } else if (metaFunction->isConstructor() && !metaFunction->isPrivate()) {
- *metaClass -= AbstractMetaClass::FinalInTargetLang;
- metaClass->setHasNonPrivateConstructor(true);
}
+ if (metaFunction->isConstructor() && !metaFunction->isPrivate()) // Including Copy CT
+ metaClass->setHasNonPrivateConstructor(true);
if (!metaFunction->isDestructor()
&& !(metaFunction->isPrivate() && metaFunction->functionType() == AbstractMetaFunction::ConstructorFunction)) {
func->setOriginalName(func->name());
func->setName(mod.renamedToName());
} else if (mod.isAccessModifier()) {
- funcRef -= AbstractMetaFunction::Friendly;
-
if (mod.isPublic())
funcRef.modifyAccess(Access::Public);
else if (mod.isProtected())
funcRef.modifyAccess(Access::Protected);
else if (mod.isPrivate())
funcRef.modifyAccess(Access::Private);
- else if (mod.isFriendly())
- funcRef += AbstractMetaFunction::Friendly;
}
-
- if (mod.isFinal())
- funcRef += AbstractMetaFunction::FinalInTargetLang;
- else if (mod.isNonFinal())
- funcRef -= AbstractMetaFunction::FinalInTargetLang;
}
}
&info, &baseContainerType);
if (templ) {
setupInheritance(templ);
- inheritTemplate(metaClass, templ, info);
+ if (!inheritTemplate(metaClass, templ, info))
+ return false;
metaClass->typeEntry()->setBaseContainerType(templ->typeEntry());
return true;
}
return true;
}
- qCWarning(lcShiboken).noquote().nospace()
- << QStringLiteral("template baseclass '%1' of '%2' is not known")
- .arg(baseClasses.constFirst(), metaClass->name());
+ qCWarning(lcShiboken, "template baseclass '%s' of '%s' is not known",
+ qPrintable(baseClasses.constFirst()),
+ qPrintable(metaClass->name()));
return false;
}
const AbstractMetaArgumentList fargs = metaFunction->arguments();
if (metaClass->isNamespace())
- *metaFunction += AbstractMetaFunction::Static;
+ metaFunction->setCppAttribute(FunctionAttribute::Static);
if (metaFunction->name() == metaClass->name()) {
metaFunction->setFunctionType(AbstractMetaFunction::ConstructorFunction);
if (fargs.size() == 1) {
if (functionItem->isFriend())
return nullptr;
- const bool deprecated = functionItem->isDeprecated();
+ const auto cppAttributes = functionItem->attributes();
+ const bool deprecated = cppAttributes.testFlag(FunctionAttribute::Deprecated);
if (deprecated && m_skipDeprecated) {
rejectFunction(functionItem, currentClass,
AbstractMetaBuilder::GenerationDisabled, u" is deprecated."_s);
AbstractMetaFunction::Flags flags;
auto *metaFunction = new AbstractMetaFunction(functionName);
+ metaFunction->setCppAttributes(cppAttributes);
const QByteArray cSignature = signature.toUtf8();
const QString unresolvedSignature =
QString::fromUtf8(QMetaObject::normalizedSignature(cSignature.constData()));
if (functionItem->isHiddenFriend())
flags.setFlag(AbstractMetaFunction::Flag::HiddenFriend);
metaFunction->setSourceLocation(functionItem->sourceLocation());
- if (deprecated)
- *metaFunction += AbstractMetaFunction::Deprecated;
// Additional check for assignment/move assignment down below
metaFunction->setFunctionType(functionTypeFromCodeModel(functionItem->functionType()));
metaFunction->setConstant(functionItem->isConstant());
metaFunction->setExceptionSpecification(functionItem->exceptionSpecification());
- if (functionItem->isAbstract())
- *metaFunction += AbstractMetaFunction::Abstract;
-
- if (functionItem->isVirtual()) {
- *metaFunction += AbstractMetaFunction::VirtualCppMethod;
- if (functionItem->isOverride())
- *metaFunction += AbstractMetaFunction::OverriddenCppMethod;
- if (functionItem->isFinal())
- *metaFunction += AbstractMetaFunction::FinalCppMethod;
- } else {
- *metaFunction += AbstractMetaFunction::FinalInTargetLang;
- }
-
- if (functionItem->isInvokable())
- *metaFunction += AbstractMetaFunction::Invokable;
-
- if (functionItem->isStatic()) {
- *metaFunction += AbstractMetaFunction::Static;
- *metaFunction += AbstractMetaFunction::FinalInTargetLang;
- }
-
// Access rights
metaFunction->setAccess(functionItem->accessPolicy());
metaFunction->setType(AbstractMetaType::createVoid());
break;
case AbstractMetaFunction::ConstructorFunction:
- metaFunction->setExplicit(functionItem->isExplicit());
metaFunction->setName(currentClass->name());
metaFunction->setType(AbstractMetaType::createVoid());
break;
// If an invalid argument has a default value, simply remove it
// unless the function is virtual (since the override in the
// wrapper can then not correctly be generated).
- if (arg->defaultValue() && !functionItem->isVirtual()) {
+ if (arg->defaultValue()
+ && !functionItem->attributes().testFlag(FunctionAttribute::Virtual)) {
if (!currentClass || currentClass->typeEntry()->generateCode()) {
const QString signature = qualifiedFunctionSignatureWithType(functionItem, className);
qCWarning(lcShiboken, "%s",
AbstractMetaClassCPtr AbstractMetaBuilderPrivate::resolveTypeSystemTypeDef(const AbstractMetaType &t) const
{
if (t.hasInstantiations()) {
- auto pred = [t](const TypeClassEntry &e) { return e.type.equals(t); };
+ auto pred = [t](const TypeClassEntry &e) { return e.type == t; };
auto it = std::find_if(m_typeSystemTypeDefs.cbegin(), m_typeSystemTypeDefs.cend(), pred);
if (it != m_typeSystemTypeDefs.cend())
return it->klass;
bool isConstCharStarCase =
oneDimensionalArrayOfUnspecifiedSize
&& typeInfo.qualifiedName().size() == 1
- && typeInfo.qualifiedName().at(0) == QStringLiteral("char")
+ && typeInfo.qualifiedName().at(0) == "char"_L1
&& typeInfo.indirections() == 0
&& typeInfo.isConstant()
&& typeInfo.referenceType() == NoReference
// This is a very lame way to handle expression evaluation,
// but it is not critical and will do for the time being.
- static const QRegularExpression variableNameRegExp(QStringLiteral("^[a-zA-Z_][a-zA-Z0-9_]*$"));
+ static const QRegularExpression variableNameRegExp("^[a-zA-Z_][a-zA-Z0-9_]*$"_L1);
Q_ASSERT(variableNameRegExp.isValid());
if (!variableNameRegExp.match(stringValue).hasMatch()) {
ok = true;
AbstractMetaClassPtr
AbstractMetaBuilder::inheritTemplateClass(const ComplexTypeEntryPtr &te,
const AbstractMetaClassCPtr &templateClass,
- const AbstractMetaTypeList &templateTypes,
- InheritTemplateFlags flags)
+ const AbstractMetaTypeList &templateTypes)
{
auto result = std::make_shared<AbstractMetaClass>();
result->setTypeDef(true);
result->setTypeEntry(te);
if (!AbstractMetaBuilderPrivate::inheritTemplate(result, templateClass,
- templateTypes, flags)) {
+ templateTypes)) {
return {};
}
AbstractMetaBuilderPrivate::inheritTemplateFunctions(result);
return result;
}
+
+static std::optional<AbstractMetaType>
+ inheritTemplateParameter(const AbstractMetaClassPtr &subclass,
+ const AbstractMetaClassCPtr &templateClass,
+ const TypeInfo &info, QString *errorMessage)
+{
+ QString typeName = info.qualifiedName().join("::"_L1);
+ TypeDatabase *typeDb = TypeDatabase::instance();
+ TypeEntryPtr t;
+ // Check for a non-type template integer parameter, that is, for a base
+ // "template <int R, int C> Matrix<R, C>" and subclass
+ // "typedef Matrix<2,3> Matrix2x3;". If so, create dummy entries of
+ // EnumValueTypeEntry for the integer values encountered on the fly.
+ if (isNumber(typeName)) {
+ t = typeDb->findType(typeName);
+ if (!t) {
+ auto parent = typeSystemTypeEntry(subclass->typeEntry());
+ t = TypeDatabase::instance()->addConstantValueTypeEntry(typeName, parent);
+ }
+ } else {
+ QStringList possibleNames;
+ possibleNames << subclass->qualifiedCppName() + "::"_L1 + typeName;
+ possibleNames << templateClass->qualifiedCppName() + "::"_L1 + typeName;
+ if (subclass->enclosingClass())
+ possibleNames << subclass->enclosingClass()->qualifiedCppName() + "::"_L1 + typeName;
+ possibleNames << typeName;
+
+ for (const QString &possibleName : std::as_const(possibleNames)) {
+ t = typeDb->findType(possibleName);
+ if (t)
+ break;
+ }
+ }
+
+ if (!t) {
+ *errorMessage = msgIgnoringTemplateParameter(typeName,
+ "The corresponding type was not found in the typesystem.");
+ return std::nullopt;
+ }
+
+ if (t->isContainer()) {
+ *errorMessage = msgIgnoringTemplateParameter(typeName,
+ "Template inheritance from nested containers is not supported");
+ return std::nullopt;
+ }
+ AbstractMetaType result(t);
+ result.setConstant(info.isConstant());
+ result.setReferenceType(info.referenceType());
+ result.setIndirectionsV(info.indirectionsV());
+ result.decideUsagePattern();
+ return result;
+}
+
bool AbstractMetaBuilderPrivate::inheritTemplate(const AbstractMetaClassPtr &subclass,
const AbstractMetaClassCPtr &templateClass,
const TypeInfo &info)
{
AbstractMetaTypeList templateTypes;
+ QString errorMessage;
for (const TypeInfo &i : info.instantiations()) {
- QString typeName = i.qualifiedName().join(u"::"_s);
- TypeDatabase *typeDb = TypeDatabase::instance();
- TypeEntryPtr t;
- // Check for a non-type template integer parameter, that is, for a base
- // "template <int R, int C> Matrix<R, C>" and subclass
- // "typedef Matrix<2,3> Matrix2x3;". If so, create dummy entries of
- // EnumValueTypeEntry for the integer values encountered on the fly.
- if (isNumber(typeName)) {
- t = typeDb->findType(typeName);
- if (!t) {
- auto parent = typeSystemTypeEntry(subclass->typeEntry());
- t = TypeDatabase::instance()->addConstantValueTypeEntry(typeName, parent);
- }
- } else {
- QStringList possibleNames;
- possibleNames << subclass->qualifiedCppName() + u"::"_s + typeName;
- possibleNames << templateClass->qualifiedCppName() + u"::"_s + typeName;
- if (subclass->enclosingClass())
- possibleNames << subclass->enclosingClass()->qualifiedCppName() + u"::"_s + typeName;
- possibleNames << typeName;
-
- for (const QString &possibleName : std::as_const(possibleNames)) {
- t = typeDb->findType(possibleName);
- if (t)
- break;
- }
- }
-
- if (t) {
- AbstractMetaType temporaryType(t);
- temporaryType.setConstant(i.isConstant());
- temporaryType.setReferenceType(i.referenceType());
- temporaryType.setIndirectionsV(i.indirectionsV());
- temporaryType.decideUsagePattern();
- templateTypes << temporaryType;
+ const auto typeO = inheritTemplateParameter(subclass, templateClass, i, &errorMessage);
+ if (typeO.has_value()) {
+ templateTypes.append(typeO.value());
} else {
- qCWarning(lcShiboken).noquote().nospace()
- << "Ignoring template parameter " << typeName << " from "
- << info.toString() << ". The corresponding type was not found in the typesystem.";
+ errorMessage = msgInheritTemplateIssue(subclass, info, errorMessage);
+ qCWarning(lcShiboken, "%s", qPrintable(errorMessage));
}
}
+ if (templateTypes.isEmpty()) {
+ errorMessage = msgInheritTemplateIssue(subclass, info,
+ "No template parameters could be inherited"_L1);
+ qCWarning(lcShiboken, "%s", qPrintable(errorMessage));
+ return false;
+ }
return inheritTemplate(subclass, templateClass, templateTypes);
}
bool AbstractMetaBuilderPrivate::inheritTemplate(const AbstractMetaClassPtr &subclass,
const AbstractMetaClassCPtr &templateClass,
- const AbstractMetaTypeList &templateTypes,
- InheritTemplateFlags flags)
+ const AbstractMetaTypeList &templateTypes)
{
subclass->setTemplateBaseClass(templateClass);
- if (flags.testFlag(InheritTemplateFlag::SetEnclosingClass))
- subclass->setEnclosingClass(templateClass->enclosingClass());
subclass->setTemplateBaseClassInstantiations(templateTypes);
subclass->setBaseClass(templateClass->baseClass());
return true;
if (!result.isValid() && graph.nodeCount()) {
QTemporaryFile tempFile(QDir::tempPath() + u"/cyclic_depXXXXXX.dot"_s);
tempFile.setAutoRemove(false);
- tempFile.open();
- graph.dumpDot(tempFile.fileName(),
- [] (const AbstractMetaClassCPtr &c) { return c->name(); });
+ const bool ok = tempFile.open();
+ if (ok) {
+ graph.dumpDot(tempFile.fileName(),
+ [] (const AbstractMetaClassCPtr &c) { return c->name(); });
+ }
QString message;
QTextStream str(&message);
str << "Cyclic dependency of classes found:";
for (const auto &c : result.cyclic)
str << ' ' << c->name();
- str << ". Graph can be found at \"" << QDir::toNativeSeparators(tempFile.fileName()) << '"';
+ str << '.';
+ if (ok) {
+ str << " Graph can be found at \""
+ << QDir::toNativeSeparators(tempFile.fileName()) << '"';
+ }
qCWarning(lcShiboken, "%s", qPrintable(message));
}
const AbstractMetaFunctionCList &globalFunctions() const;
const AbstractMetaEnumList &globalEnums() const;
const QHash<TypeEntryCPtr, AbstractMetaEnum> &typeEntryToEnumsHash() const;
+ const QMultiHash<QString, QString> &typedefTargetToName() const;
bool build(const QByteArrayList &arguments,
ApiExtractorFlags apiExtractorFlags = {},
const AbstractMetaTypeList &templateTypes);
static AbstractMetaClassPtr
- inheritTemplateClass(const ComplexTypeEntryPtr &te,
- const AbstractMetaClassCPtr &templateClass,
- const AbstractMetaTypeList &templateTypes,
- InheritTemplateFlags flags = {});
+ inheritTemplateClass(const ComplexTypeEntryPtr &te,
+ const AbstractMetaClassCPtr &templateClass,
+ const AbstractMetaTypeList &templateTypes);
/// Performs a template specialization of the member function.
/// \param function Member function
#include <QtCore/QFileInfo>
#include <QtCore/QList>
#include <QtCore/QMap>
+#include <QtCore/QMultiHash>
#include <QtCore/QSet>
#include <optional>
AbstractMetaClassPtr traverseTypeDef(const FileModelItem &dom,
const TypeDefModelItem &typeDef,
const AbstractMetaClassPtr ¤tClass);
+ AbstractMetaClassPtr traverseTypeDefHelper(const FileModelItem &dom,
+ const TypeDefModelItem &typeDef,
+ const AbstractMetaClassPtr ¤tClass);
void traverseTypesystemTypedefs();
AbstractMetaClassPtr traverseClass(const FileModelItem &dom,
const ClassModelItem &item,
const TypeInfo &info);
static bool inheritTemplate(const AbstractMetaClassPtr &subclass,
const AbstractMetaClassCPtr &templateClass,
- const AbstractMetaTypeList &templateTypes,
- InheritTemplateFlags flags = {});
+ const AbstractMetaTypeList &templateTypes);
static AbstractMetaFunctionPtr
inheritTemplateFunction(const AbstractMetaFunctionCPtr &function,
QFileInfoList m_globalHeaders;
QStringList m_headerPaths;
mutable QHash<QString, Include> m_resolveIncludeHash;
+ QMultiHash<QString, QString> m_typedefTargetToName;
QList<TypeClassEntry> m_typeSystemTypeDefs; // look up metatype->class for type system typedefs
ApiExtractorFlags m_apiExtractorFlags;
bool m_skipDeprecated = false;
AbstractMetaFunctionPrivate()
: m_constant(false),
m_reverse(false),
- m_explicit(false),
m_pointerOperator(false),
m_isCallOperator(false)
{
AddedFunctionPtr m_addedFunction;
SourceLocation m_sourceLocation;
AbstractMetaFunction::Attributes m_attributes;
+ FunctionAttributes m_cppAttributes;
AbstractMetaFunction::Flags m_flags;
uint m_constant : 1;
uint m_reverse : 1;
- uint m_explicit : 1;
uint m_pointerOperator : 1;
uint m_isCallOperator : 1;
mutable int m_cachedOverloadNumber = TypeSystem::OverloadNumberUnset;
setAccess(Access::Public);
break;
}
- AbstractMetaFunction::Attributes atts = AbstractMetaFunction::FinalInTargetLang;
+ AbstractMetaFunction::Attributes atts;
if (addedFunc->isStatic())
- atts |= AbstractMetaFunction::Static;
+ setCppAttribute(FunctionAttribute::Static);
if (addedFunc->isClassMethod())
atts |= AbstractMetaFunction::ClassMethod;
setAttributes(atts);
bool AbstractMetaFunction::isExplicit() const
{
- return d->m_explicit;
+ return d->m_cppAttributes.testFlag(FunctionAttribute::Explicit);
}
void AbstractMetaFunction::setExplicit(bool isExplicit)
{
- d->m_explicit = isExplicit;
+ d->m_cppAttributes.setFlag(FunctionAttribute::Explicit, isExplicit);
}
bool AbstractMetaFunction::returnsBool() const
d->m_attributes.setFlag(attribute, false);
}
+FunctionAttributes AbstractMetaFunction::cppAttributes() const
+{
+ return d->m_cppAttributes;
+}
+
+void AbstractMetaFunction::setCppAttributes(FunctionAttributes a)
+{
+ d->m_cppAttributes = a;
+}
+
+void AbstractMetaFunction::setCppAttribute(FunctionAttribute a, bool on)
+{
+ d->m_cppAttributes.setFlag(a, on);
+}
+
AbstractMetaFunction::Flags AbstractMetaFunction::flags() const
{
return d->m_flags;
result |= EqualImplementor;
// Attributes
- if (attributes() == other->attributes())
+ if (attributes() == other->attributes() && cppAttributes() == other->cppAttributes())
result |= EqualAttributes;
// Compare types
{
auto *cpy = new AbstractMetaFunction;
cpy->setAttributes(attributes());
+ auto ca = cppAttributes();
+ // Historical bug: explicit was not copied! (causing nontypetemplate_test.py fail)
+ ca.setFlag(FunctionAttribute::Explicit, false);
+ cpy->setCppAttributes(ca);
cpy->setFlags(flags());
cpy->setAccess(access());
cpy->setName(name());
return d->m_addedFunction && !d->m_addedFunction->isDeclaration();
}
+bool AbstractMetaFunction::isUserAddedPythonOverride() const
+{
+ return d->m_addedFunction && d->m_addedFunction->isPythonOverride();
+}
+
bool AbstractMetaFunction::isUserDeclared() const
{
return d->m_addedFunction && d->m_addedFunction->isDeclaration();
bool AbstractMetaFunction::isDeprecated() const
{
const auto &mods = modifications(declaringClass());
- return d->m_attributes.testFlag(Attribute::Deprecated)
+
+ return d->m_cppAttributes.testFlag(FunctionAttribute::Deprecated)
? std::none_of(mods.cbegin(), mods.cend(), modifiedUndeprecated)
: std::any_of(mods.cbegin(), mods.cend(), modifiedDeprecated);
}
QString AbstractMetaFunction::debugSignature() const
{
QString result;
- const bool isOverride = attributes() & AbstractMetaFunction::OverriddenCppMethod;
- const bool isFinal = attributes() & AbstractMetaFunction::FinalCppMethod;
- if (!isOverride && !isFinal && (attributes() & AbstractMetaFunction::VirtualCppMethod))
+ const auto attributes = cppAttributes();
+ const bool isOverride = attributes.testFlag(FunctionAttribute::Override);
+ const bool isFinal = attributes.testFlag(FunctionAttribute::Final);
+ if (!isOverride && !isFinal && (attributes.testFlag(FunctionAttribute::Virtual)))
result += u"virtual "_s;
if (d->m_implementingClass)
result += d->m_implementingClass->qualifiedCppName() + u"::"_s;
d->m_typeEntry = typeEntry;
}
+QString AbstractMetaFunction::targetLangPackage() const
+{
+ if (d->m_addedFunction != nullptr)
+ return d->m_addedFunction->targetLangPackage();
+ if (d->m_class != nullptr)
+ return d->m_class->typeEntry()->targetLangPackage();
+ if (d->m_typeEntry != nullptr)
+ return d->m_typeEntry->targetLangPackage();
+ return {};
+}
+
bool AbstractMetaFunction::isCallOperator() const
{
return d->m_name == u"operator()";
bool AbstractMetaFunction::isVirtual() const
{
- return d->m_attributes.testFlag(AbstractMetaFunction::VirtualCppMethod);
+ return d->m_cppAttributes.testFlag(FunctionAttribute::Virtual);
}
QString AbstractMetaFunctionPrivate::modifiedName(const AbstractMetaFunction *q) const
AbstractMetaFunctionCPtr
AbstractMetaFunction::find(const AbstractMetaFunctionCList &haystack,
- QStringView needle)
+ QAnyStringView needle)
{
for (const auto &f : haystack) {
if (f->name() == needle)
bool AbstractMetaFunction::injectedCodeCallsPythonOverride() const
{
static const QRegularExpression
- overrideCallRegexCheck(QStringLiteral(R"(PyObject_Call\s*\(\s*%PYTHON_METHOD_OVERRIDE\s*,)"));
+ overrideCallRegexCheck(R"(PyObject_Call\s*\(\s*%PYTHON_METHOD_OVERRIDE\s*,)"_L1);
Q_ASSERT(overrideCallRegexCheck.isValid());
return injectedCodeContains(overrideCallRegexCheck, TypeSystem::CodeSnipPositionAny,
TypeSystem::NativeCode);
{
if (language == TypeSystem::TargetLangCode) {
static const QRegularExpression
- retValAttributionRegexCheck_target(QStringLiteral(R"(%PYARG_0\s*=[^=]\s*.+)"));
+ retValAttributionRegexCheck_target(R"(%PYARG_0\s*=[^=]\s*.+)"_L1);
Q_ASSERT(retValAttributionRegexCheck_target.isValid());
return injectedCodeContains(retValAttributionRegexCheck_target, TypeSystem::CodeSnipPositionAny, language);
}
static const QRegularExpression
- retValAttributionRegexCheck_native(QStringLiteral(R"(%0\s*=[^=]\s*.+)"));
+ retValAttributionRegexCheck_native(R"(%0\s*=[^=]\s*.+)"_L1);
Q_ASSERT(retValAttributionRegexCheck_native.isValid());
return injectedCodeContains(retValAttributionRegexCheck_native, TypeSystem::CodeSnipPositionAny, language);
}
debug << " [userAdded]";
if (isUserDeclared())
debug << " [userDeclared]";
- if (d->m_explicit)
+ if (d->m_cppAttributes.testFlag(FunctionAttribute::Explicit))
debug << " [explicit]";
- if (attributes().testFlag(AbstractMetaFunction::Deprecated))
+ if (d->m_cppAttributes.testFlag(FunctionAttribute::Deprecated))
debug << " [deprecated]";
if (d->m_pointerOperator)
debug << " [operator->]";
enum Attribute {
None = 0x00000000,
- Friendly = 0x00000001,
-
- Abstract = 0x00000002,
- Static = 0x00000004,
ClassMethod = 0x00000008,
- FinalInTargetLang = 0x00000010,
-
GetterFunction = 0x00000020,
SetterFunction = 0x00000040,
PropertyResetter = 0x00000400,
PropertyNotify = 0x00000800,
- Invokable = 0x00001000,
-
- VirtualCppMethod = 0x00010000,
- OverriddenCppMethod = 0x00020000,
- FinalCppMethod = 0x00040000,
// Add by meta builder (implicit constructors, inherited methods, etc)
AddedMethod = 0x001000000,
- Deprecated = 0x002000000 // Code annotation
};
Q_DECLARE_FLAGS(Attributes, Attribute)
Q_FLAG(Attribute)
void operator+=(Attribute attribute);
void operator-=(Attribute attribute);
+ FunctionAttributes cppAttributes() const;
+ void setCppAttributes(FunctionAttributes a);
+ void setCppAttribute(FunctionAttribute a, bool on = true);
+
enum class Flag { // Internal flags not relevant for comparing functions
// Binary operator whose leading/trailing argument was removed by metabuilder
OperatorLeadingClassArgumentRemoved = 0x1,
Flags flags() const;
void setFlags(Flags f);
- bool isFinalInTargetLang() const;
bool isAbstract() const;
bool isClassMethod() const;
bool isStatic() const;
- bool isInvokable() const;
bool isPropertyReader() const;
bool isPropertyWriter() const;
bool isPropertyResetter() const;
/// Returns true if the AbstractMetaFunction was added by the user via the type system description.
bool isUserAdded() const;
+ bool isUserAddedPythonOverride() const;
/// Returns true if the AbstractMetaFunction was declared by the user via
/// the type system description.
bool isUserDeclared() const;
void setPropertySpecIndex(int i);
FunctionTypeEntryPtr typeEntry() const;
-
void setTypeEntry(const FunctionTypeEntryPtr &typeEntry);
+ QString targetLangPackage() const;
+
bool isCallOperator() const;
static AbstractMetaFunctionCPtr
- find(const AbstractMetaFunctionCList &haystack, QStringView needle);
+ find(const AbstractMetaFunctionCList &haystack, QAnyStringView needle);
bool matches(OperatorQueryOptions) const;
QScopedPointer<AbstractMetaFunctionPrivate> d;
};
-inline bool AbstractMetaFunction::isFinalInTargetLang() const
-{
- return attributes().testFlag(FinalInTargetLang);
-}
-
inline bool AbstractMetaFunction::isAbstract() const
{
- return attributes().testFlag(Abstract);
+ return cppAttributes().testFlag(FunctionAttribute::Abstract);
}
inline bool AbstractMetaFunction::isStatic() const
{
- return attributes().testFlag(Static);
+ return cppAttributes().testFlag(FunctionAttribute::Static);
}
inline bool AbstractMetaFunction::isClassMethod() const
return attributes().testFlag(ClassMethod);
}
-inline bool AbstractMetaFunction::isInvokable() const
-{
- return attributes().testFlag(Invokable);
-}
-
inline bool AbstractMetaFunction::isPropertyReader() const
{
return attributes().testFlag(PropertyReader);
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "abstractmetalang.h"
+#include "anystringview_helpers.h"
#include "abstractmetalang_helpers.h"
#include "abstractmetaargument.h"
#include "abstractmetaenum.h"
AbstractMetaClassCPtr m_templateBaseClass;
AbstractMetaFunctionCList m_functions;
+ AbstractMetaFunctionCList m_userAddedPythonOverrides;
AbstractMetaFieldList m_fields;
AbstractMetaEnumList m_enums;
QList<QPropertySpec> m_propertySpecs;
FunctionQueryOptions default_flags = FunctionQueryOption::NormalFunctions
| FunctionQueryOption::Visible | FunctionQueryOption::NotRemoved;
- // Only public functions in final classes
- // default_flags |= isFinal() ? WasPublic : 0;
- FunctionQueryOptions public_flags;
- if (isFinalInTargetLang())
- public_flags |= FunctionQueryOption::WasPublic;
-
// Constructors
AbstractMetaFunctionCList returned = queryFunctions(FunctionQueryOption::AnyConstructor
- | default_flags | public_flags);
-
- // Final functions
- returned += queryFunctions(FunctionQueryOption::FinalInTargetLangFunctions
- | FunctionQueryOption::NonStaticFunctions
- | default_flags | public_flags);
+ | default_flags);
- // Virtual functions
- returned += queryFunctions(FunctionQueryOption::VirtualInTargetLangFunctions
- | FunctionQueryOption::NonStaticFunctions
- | default_flags | public_flags);
+ returned += queryFunctions(FunctionQueryOption::NonStaticFunctions
+ | default_flags);
// Static functions
returned += queryFunctions(FunctionQueryOption::StaticFunctions
- | default_flags | public_flags);
+ | default_flags);
// Empty, private functions, since they aren't caught by the other ones
returned += queryFunctions(FunctionQueryOption::Empty | FunctionQueryOption::Invisible);
return d->m_functions;
}
+const AbstractMetaFunctionCList &AbstractMetaClass::userAddedPythonOverrides() const
+{
+ return d->m_userAddedPythonOverrides;
+}
+
void AbstractMetaClassPrivate::sortFunctions()
{
std::sort(m_functions.begin(), m_functions.end(), function_sorter);
// to function properly. Such as function modifications
nonConstF->setImplementingClass(klass);
- klass->d->addFunction(function);
+ if (function->isUserAddedPythonOverride()) {
+ nonConstF->setConstant(false);
+ nonConstF->setCppAttribute(FunctionAttribute::Static);
+ klass->d->m_userAddedPythonOverrides.append(function);
+ } else {
+ klass->d->addFunction(function);
+ }
}
bool AbstractMetaClass::hasSignal(const AbstractMetaFunction *other) const
return bool(findFunction(str));
}
-AbstractMetaFunctionCPtr AbstractMetaClass::findFunction(QStringView functionName) const
+AbstractMetaFunctionCPtr AbstractMetaClass::findFunction(QAnyStringView functionName) const
{
return AbstractMetaFunction::find(d->m_functions, functionName);
}
-AbstractMetaFunctionCList AbstractMetaClass::findFunctions(QStringView functionName) const
+AbstractMetaFunctionCList AbstractMetaClass::findFunctions(QAnyStringView functionName) const
{
AbstractMetaFunctionCList result;
std::copy_if(d->m_functions.cbegin(), d->m_functions.cend(),
auto *f = createFunction(q->name(), t, access, arguments, AbstractMetaType::createVoid(), q);
if (access != Access::Private)
m_hasNonPrivateConstructor = true;
- f->setAttributes(AbstractMetaFunction::FinalInTargetLang
- | AbstractMetaFunction::AddedMethod);
+ f->setAttributes(AbstractMetaFunction::AddedMethod);
addFunction(AbstractMetaFunctionCPtr(f));
}
if (query.testFlag(FunctionQueryOption::Visible) && f->isPrivate())
return false;
- if (query.testFlag(FunctionQueryOption::VirtualInTargetLangFunctions) && f->isFinalInTargetLang())
- return false;
-
if (query.testFlag(FunctionQueryOption::Invisible) && !f->isPrivate())
return false;
if (query.testFlag(FunctionQueryOption::Empty) && !f->isEmptyFunction())
return false;
- if (query.testFlag(FunctionQueryOption::WasPublic) && !f->wasPublic())
- return false;
-
if (query.testFlag(FunctionQueryOption::ClassImplements) && f->ownerClass() != f->implementingClass())
return false;
- if (query.testFlag(FunctionQueryOption::FinalInTargetLangFunctions) && !f->isFinalInTargetLang())
- return false;
-
if (query.testFlag(FunctionQueryOption::VirtualInCppFunctions) && !f->isVirtual())
return false;
}
for (const auto &superClassC : d->m_baseClasses) {
+ for (const auto &pof : superClassC->userAddedPythonOverrides()) {
+ auto *clonedPof = pof->copy();
+ clonedPof->setOwnerClass(klass);
+ d->m_userAddedPythonOverrides.append(AbstractMetaFunctionCPtr{clonedPof});
+ }
+
auto superClass = std::const_pointer_cast<AbstractMetaClass>(superClassC);
AbstractMetaClass::fixFunctions(superClass);
// Since we always traverse the complete hierarchy we are only
// interrested in what each super class implements, not what
// we may have propagated from their base classes again.
AbstractMetaFunctionCList superFuncs;
- // Super classes can never be final
- if (superClass->isFinalInTargetLang()) {
- qCWarning(lcShiboken).noquote().nospace()
- << "Final class '" << superClass->name() << "' set to non-final, as it is extended by other classes";
- *superClass -= AbstractMetaClass::FinalInTargetLang;
- }
superFuncs = superClass->queryFunctions(FunctionQueryOption::ClassImplements);
// We are not interested in signals as no bindings are generated for them;
// they cause documentation warnings.
if (cmp & AbstractMetaFunction::EqualArguments) {
// Set "override" in case it was not spelled out (since it
// is then not detected by clang parsing).
- const auto attributes = cf->attributes();
- if (cf->isVirtual()
- && !attributes.testFlag(AbstractMetaFunction::OverriddenCppMethod)
- && !attributes.testFlag(AbstractMetaFunction::FinalCppMethod)) {
- *f += AbstractMetaFunction::OverriddenCppMethod;
- }
- // Same function, propegate virtual...
- if (!(cmp & AbstractMetaFunction::EqualAttributes)) {
- if (!f->isEmptyFunction()) {
- if (!sf->isFinalInTargetLang() && f->isFinalInTargetLang()) {
- *f -= AbstractMetaFunction::FinalInTargetLang;
- }
-#if 0
- if (!f->isFinalInTargetLang() && f->isPrivate()) {
- f->setFunctionType(AbstractMetaFunction::EmptyFunction);
- f->setVisibility(AbstractMetaAttributes::Protected);
- *f += AbstractMetaAttributes::FinalInTargetLang;
- qCWarning(lcShiboken).noquote().nospace()
- << QStringLiteral("private virtual function '%1' in '%2'")
- .arg(f->signature(), f->implementingClass()->name());
- }
-#endif
- }
+ const auto attributes = cf->cppAttributes();
+ if (attributes.testFlag(FunctionAttribute::Virtual)
+ && !attributes.testFlag(FunctionAttribute::Override)
+ && !attributes.testFlag(FunctionAttribute::Final)) {
+ f->setCppAttribute(FunctionAttribute::Override);
}
if (f->access() != sf->access()) {
// Private overrides of abstract functions have to go into the class or
// the subclasses will not compile as non-abstract classes.
// But they don't need to be implemented, since they can never be called.
- if (f->isPrivate()) {
+ if (f->isPrivate())
f->setFunctionType(AbstractMetaFunction::EmptyFunction);
- *f += AbstractMetaFunction::FinalInTargetLang;
- }
}
// Set the class which first declares this function, afawk
f->setDeclaringClass(sf->declaringClass());
-
- if (sf->isFinalInTargetLang() && !sf->isPrivate() && !f->isPrivate() && !sf->isStatic() && !f->isStatic()) {
- // Shadowed funcion, need to make base class
- // function non-virtual
- if (f->implementingClass() != sf->implementingClass()
- && inheritsFrom(f->implementingClass(), sf->implementingClass())) {
-
- // Check whether the superclass method has been redefined to non-final
-
- bool hasNonFinalModifier = false;
- bool isBaseImplPrivate = false;
- const FunctionModificationList &mods = sf->modifications(sf->implementingClass());
- for (const FunctionModification &mod : mods) {
- if (mod.isNonFinal()) {
- hasNonFinalModifier = true;
- break;
- }
- if (mod.isPrivate()) {
- isBaseImplPrivate = true;
- break;
- }
- }
-
- if (!hasNonFinalModifier && !isBaseImplPrivate) {
- qCWarning(lcShiboken, "%s",
- qPrintable(msgShadowingFunction(sf.get(), f.get())));
- }
- }
- }
-
}
if (cmp & AbstractMetaFunction::EqualDefaultValueOverload) {
switch (d->m_typeEntry->type()) {
case TypeEntry::NamespaceType:
case TypeEntry::SmartPointerType:
+ case TypeEntry::ContainerType:
return false;
default:
break;
/// Target language base name or complete Target language package.class name.
template <class It>
-static It findClassHelper(It begin, It end, QStringView name)
+static It findClassHelper(It begin, It end, QAnyStringView name)
{
if (name.isEmpty() || begin == end)
return end;
- if (name.contains(u'.')) { // Search target lang name
+ if (asv_contains(name,'.')) { // Search target lang name
for (auto it = begin; it != end; ++it) {
if ((*it)->fullName() == name)
return it;
return it;
}
- if (name.contains(u"::")) // Qualified, cannot possibly match name
+ if (asv_contains(name, "::")) // Qualified, cannot possibly match name
return end;
for (auto it = begin; it != end; ++it) {
}
AbstractMetaClassPtr AbstractMetaClass::findClass(const AbstractMetaClassList &classes,
- QStringView name)
+ QAnyStringView name)
{
auto it =findClassHelper(classes.cbegin(), classes.cend(), name);
return it != classes.cend() ? *it : nullptr;
}
AbstractMetaClassCPtr AbstractMetaClass::findClass(const AbstractMetaClassCList &classes,
- QStringView name)
+ QAnyStringView name)
{
auto it = findClassHelper(classes.cbegin(), classes.cend(), name);
return it != classes.cend() ? *it : nullptr;
}));
}
-bool inheritsFrom(const AbstractMetaClassCPtr &c, const QString &name)
+bool inheritsFrom(const AbstractMetaClassCPtr &c, QAnyStringView name)
{
if (c->qualifiedCppName() == name)
return true;
return debug;
}
-QDebug operator<<(QDebug d, const AbstractMetaClassCPtr &ac)
+void formatMetaClass(QDebug &ddebug, const AbstractMetaClass *ac)
{
- QDebugStateSaver saver(d);
- d.noquote();
- d.nospace();
- d << "AbstractMetaClass(";
- if (ac) {
- ac->format(d);
- if (d.verbosity() > 2)
- ac->formatMembers(d);
+ QDebugStateSaver saver(ddebug);
+ ddebug.noquote();
+ ddebug.nospace();
+ ddebug << "AbstractMetaClass(";
+ if (ac != nullptr) {
+ ac->format(ddebug);
+ if (ddebug.verbosity() > 2)
+ ac->formatMembers(ddebug);
} else {
- d << '0';
+ ddebug << '0';
}
- d << ')';
+ ddebug << ')';
+}
+
+QDebug operator<<(QDebug d, const AbstractMetaClassCPtr &ac)
+{
+ formatMetaClass(d, ac.get());
return d;
}
+
+QDebug operator<<(QDebug d, const AbstractMetaClassPtr &ac)
+{
+ formatMetaClass(d, ac.get());
+ return d;
+}
+
+QDebug operator<<(QDebug d, const AbstractMetaClass *ac)
+{
+ formatMetaClass(d, ac);
+ return d;
+}
+
#endif // !QT_NO_DEBUG_STREAM
~AbstractMetaClass();
const AbstractMetaFunctionCList &functions() const;
+ const AbstractMetaFunctionCList &userAddedPythonOverrides() const;
void setFunctions(const AbstractMetaFunctionCList &functions);
static void addFunction(const AbstractMetaClassPtr &klass,
const AbstractMetaFunctionCPtr &function);
bool hasFunction(const QString &str) const;
- AbstractMetaFunctionCPtr findFunction(QStringView functionName) const;
- AbstractMetaFunctionCList findFunctions(QStringView functionName) const;
+ AbstractMetaFunctionCPtr findFunction(QAnyStringView functionName) const;
+ AbstractMetaFunctionCList findFunctions(QAnyStringView functionName) const;
AbstractMetaFunctionCPtr findOperatorBool() const;
// Find a Qt-style isNull() method suitable for nb_bool
AbstractMetaFunctionCPtr findQtIsNullMethod() const;
bool avoidProtectedHack);
static AbstractMetaClassPtr findClass(const AbstractMetaClassList &classes,
- QStringView name);
+ QAnyStringView name);
static AbstractMetaClassCPtr findClass(const AbstractMetaClassCList &classes,
- QStringView name);
+ QAnyStringView name);
static AbstractMetaClassPtr findClass(const AbstractMetaClassList &classes,
const TypeEntryCPtr &typeEntry);
static AbstractMetaClassCPtr findClass(const AbstractMetaClassCList &classes,
void format(QDebug &d) const;
void formatMembers(QDebug &d) const;
friend QDebug operator<<(QDebug d, const AbstractMetaClassCPtr &ac);
+ friend QDebug operator<<(QDebug d, const AbstractMetaClassPtr &ac);
+ friend QDebug operator<<(QDebug d, const AbstractMetaClass *ac);
+ friend void formatMetaClass(QDebug &, const AbstractMetaClass *);
#endif
QScopedPointer<AbstractMetaClassPrivate> d;
};
-inline bool AbstractMetaClass::isFinalInTargetLang() const
-{
- return attributes().testFlag(FinalInTargetLang);
-}
-
inline bool AbstractMetaClass::isAbstract() const
{
return attributes().testFlag(Abstract);
}
bool inheritsFrom(const AbstractMetaClassCPtr &c, const AbstractMetaClassCPtr &other);
-bool inheritsFrom(const AbstractMetaClassCPtr &c, const QString &name);
+bool inheritsFrom(const AbstractMetaClassCPtr &c, QAnyStringView name);
inline bool isQObject(const AbstractMetaClassCPtr &c)
{
- return inheritsFrom(c, QStringLiteral("QObject"));
+ return inheritsFrom(c, "QObject");
}
AbstractMetaClassCPtr findBaseClass(const AbstractMetaClassCPtr &c,
Constructors = 0x0000002, // Constructors except copy/move
CopyConstructor = 0x0000004, // Only copy constructors
//Destructors = 0x0000002, // Only destructors. Not included in class.
- FinalInTargetLangFunctions = 0x0000008, // Only functions that are non-virtual in TargetLang
ClassImplements = 0x0000020, // Only functions implemented by the current class
StaticFunctions = 0x0000080, // Only static functions
Signals = 0x0000100, // Only signals
NormalFunctions = 0x0000200, // Only functions that aren't signals
Visible = 0x0000400, // Only public and protected functions
- WasPublic = 0x0001000, // Only functions that were originally public
NonStaticFunctions = 0x0004000, // No static functions
Empty = 0x0008000, // Empty overrides of abstract functions
Invisible = 0x0010000, // Only private functions
VirtualInCppFunctions = 0x0020000, // Only functions that are virtual in C++
- VirtualInTargetLangFunctions = 0x0080000, // Only functions which are virtual in TargetLang
NotRemoved = 0x0400000, // Only functions that have not been removed
OperatorOverloads = 0x2000000, // Only functions that are operator overloads
GenerateExceptionHandling = 0x4000000,
pattern = ObjectPattern;
}
setTypeUsagePattern(pattern);
+ Q_ASSERT(pattern != ContainerPattern || !d->m_instantiations.isEmpty());
}
bool AbstractMetaTypeData::hasTemplateChildren() const
&& m_referenceType == rhs.m_referenceType && isEquivalent(rhs);
}
-bool AbstractMetaType::equals(const AbstractMetaType &rhs) const
+bool comparesEqual(const AbstractMetaType &lhs, const AbstractMetaType &rhs) noexcept
{
- return d->equals(*rhs.d);
+ return lhs.d->equals(*rhs.d);
}
bool AbstractMetaType::isEquivalent(const AbstractMetaType &rhs) const
Q_GLOBAL_STATIC(AbstractMetaTypeCache, metaTypeFromStringCache)
std::optional<AbstractMetaType>
-AbstractMetaType::fromString(QString typeSignature, QString *errorMessage)
+AbstractMetaType::fromString(const QString &typeSignatureIn, QString *errorMessage)
{
- typeSignature = typeSignature.trimmed();
+ auto &cache = *metaTypeFromStringCache();
+ auto it = cache.find(typeSignatureIn);
+ if (it != cache.end())
+ return it.value();
+
+ QString typeSignature = typeSignatureIn.trimmed();
if (typeSignature.startsWith(u"::"))
typeSignature.remove(0, 2);
- auto &cache = *metaTypeFromStringCache();
- auto it = cache.find(typeSignature);
+ it = cache.find(typeSignature);
if (it == cache.end()) {
auto metaType =
AbstractMetaBuilder::translateType(typeSignature, nullptr, {}, errorMessage);
errorMessage->prepend(msgCannotBuildMetaType(typeSignature));
return {};
}
+ if (typeSignature != typeSignatureIn)
+ cache.insert(typeSignatureIn, metaType.value());
it = cache.insert(typeSignature, metaType.value());
}
return it.value();
#include "parser/codemodel_enums.h"
#include "typedatabase_typedefs.h"
+#include <QtCore/QtCompare>
#include <QtCore/qobjectdefs.h>
#include <QtCore/QHashFunctions>
#include <QtCore/QSharedDataPointer>
bool hasTemplateChildren() const;
- bool equals(const AbstractMetaType &rhs) const;
/// Is equivalent from the POV of argument passing (differ by const ref)
bool isEquivalent(const AbstractMetaType &rhs) const;
/// \param typeSignature The string describing the type to be built.
/// \return A new AbstractMetaType object or nullopt in case of failure.
static std::optional<AbstractMetaType>
- fromString(QString typeSignature, QString *errorMessage = nullptr);
+ fromString(const QString &typeSignatureIn, QString *errorMessage = nullptr);
/// Creates an AbstractMetaType object from a TypeEntry.
static AbstractMetaType fromTypeEntry(const TypeEntryCPtr &typeEntry);
/// Creates an AbstractMetaType object from an AbstractMetaClass.
private:
friend size_t qHash(const AbstractMetaType &t, size_t seed = 0) noexcept
{ return qHash(t.typeEntry().get(), seed); }
+ friend bool comparesEqual(const AbstractMetaType &lhs,
+ const AbstractMetaType &rhs) noexcept;
+ Q_DECLARE_EQUALITY_COMPARABLE(AbstractMetaType)
friend class AbstractMetaTypeData;
QSharedDataPointer<AbstractMetaTypeData> d;
QString formatPythonSignature() const;
};
-inline bool operator==(const AbstractMetaType &t1, const AbstractMetaType &t2)
-{ return t1.equals(t2); }
-inline bool operator!=(const AbstractMetaType &t1, const AbstractMetaType &t2)
-{ return !t1.equals(t2); }
-
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug d, const AbstractMetaType &at);
QDebug operator<<(QDebug d, const AbstractMetaType *at);
using namespace Qt::StringLiterals;
-static inline QString callOperator() { return QStringLiteral("operator()"); }
+constexpr auto callOperator = "operator()"_L1;
// Helpers to split a parameter list of <add-function>, <declare-function>
// (@ denoting names), like
// "void foo(QList<X,Y> &@list@ = QList<X,Y>{1,2}, int @b@=5, ...)"
namespace AddedFunctionParser {
-bool Argument::equals(const Argument &rhs) const
-{
- return type == rhs.type && name == rhs.name && defaultValue == rhs.defaultValue;
-}
-
QDebug operator<<(QDebug d, const Argument &a)
{
QDebugStateSaver saver(d);
QStringView signature = QStringView{signatureIn}.trimmed();
// Skip past "operator()(...)"
- const auto parenSearchStartPos = signature.startsWith(callOperator())
- ? callOperator().size() : 0;
+ const auto parenSearchStartPos = signature.startsWith(callOperator)
+ ? callOperator.size() : 0;
const auto openParenPos = signature.indexOf(u'(', parenSearchStartPos);
if (openParenPos < 0) {
return AddedFunctionPtr(new AddedFunction(signature.toString(),
bool isDeclaration() const { return m_isDeclaration; } // <declare-function>
void setDeclaration(bool value) { m_isDeclaration = value; }
+ bool isPythonOverride() const { return m_isPythonOverride; }
+ void setPythonOverride(bool o) { m_isPythonOverride = o; }
+
const FunctionModificationList &modifications() const { return m_modifications; }
FunctionModificationList &modifications() { return m_modifications; }
DocModificationList &docModifications() { return m_docModifications; }
void addDocModification(const DocModification &m) { m_docModifications.append(m); }
+ QString targetLangPackage() const { return m_targetLangPackage; }
+ void setTargetLangPackage(const QString &p) { m_targetLangPackage = p; }
+
private:
QString m_name;
QList<Argument> m_arguments;
TypeInfo m_returnType;
FunctionModificationList m_modifications;
DocModificationList m_docModifications;
+ QString m_targetLangPackage;
Access m_access = Public;
bool m_isConst = false;
bool m_isClassMethod = false;
bool m_isStatic = false;
bool m_isDeclaration = false;
+ bool m_isPythonOverride = false;
};
QDebug operator<<(QDebug d, const AddedFunction::Argument &a);
#ifndef ADDEDFUNCTION_P_H
#define ADDEDFUNCTION_P_H
+#include <QtCore/QtCompare>
#include <QtCore/QList>
#include <QtCore/QString>
#include <QtCore/QStringView>
struct Argument
{
- bool equals(const Argument &rhs) const;
-
QString type;
QString name;
QString defaultValue;
+
+ friend bool comparesEqual(const Argument &lhs, const Argument &rhs) noexcept
+ {
+ return lhs.type == rhs.type && lhs.name == rhs.name
+ && lhs.defaultValue == rhs.defaultValue;
+ }
+ Q_DECLARE_EQUALITY_COMPARABLE(Argument)
};
using Arguments = QList<Argument>;
-inline bool operator==(const Argument &a1, const Argument &a2) { return a1.equals(a2); }
-inline bool operator!=(const Argument &a1, const Argument &a2) { return !a1.equals(a2); }
-
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug d, const Argument &a);
#endif
--- /dev/null
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "anystringview_helpers.h"
+
+#include <QtCore/QString> // Must go before QAnyStringView for operator<<(QTextStream,QASV)!
+#include <QtCore/QAnyStringView>
+#include <QtCore/QDebug>
+#include <QtCore/QTextStream>
+
+#include <cstring>
+
+QTextStream &operator<<(QTextStream &str, QAnyStringView asv)
+{
+ asv.visit([&str](auto s) { str << s; });
+ return str;
+}
+
+static bool asv_containsImpl(QLatin1StringView v, char c)
+{
+ return v.contains(uint16_t(c));
+}
+
+static bool asv_containsImpl(QUtf8StringView v, char c)
+{
+ return std::strchr(v.data(), c) != nullptr;
+}
+
+static bool asv_containsImpl(QStringView v, char c)
+{
+ return v.contains(uint16_t(c));
+}
+
+bool asv_contains(QAnyStringView asv, char needle)
+{
+ return asv.visit([needle](auto s) { return asv_containsImpl(s, needle); });
+}
+
+static bool asv_containsImpl(QLatin1StringView v, const char *c)
+{
+ return v.contains(QLatin1StringView(c));
+}
+static bool asv_containsImpl(QUtf8StringView v, const char *c)
+{
+ return std::strstr(v.data(), c) != nullptr;
+}
+
+static bool asv_containsImpl(QStringView v, const char *c)
+{
+ return v.contains(QLatin1StringView(c));
+}
+
+bool asv_contains(QAnyStringView asv, const char *needle)
+{
+ return asv.visit([needle](auto s) { return asv_containsImpl(s, needle); });
+}
--- /dev/null
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef ANYSTRINGVIEW_STREAM_H
+#define ANYSTRINGVIEW_STREAM_H
+
+#include <QtCore/QtClassHelperMacros>
+
+QT_FORWARD_DECLARE_CLASS(QAnyStringView)
+QT_FORWARD_DECLARE_CLASS(QTextStream)
+QT_FORWARD_DECLARE_CLASS(QDebug)
+
+QTextStream &operator<<(QTextStream &str, QAnyStringView asv);
+
+bool asv_contains(QAnyStringView asv, char needle);
+bool asv_contains(QAnyStringView asv, const char *needle);
+
+#endif // ANYSTRINGVIEW_STREAM_H
}
const QString pattern = QDir::tempPath() + u'/'
- + m_cppFileNames.constFirst().baseName()
- + QStringLiteral("_XXXXXX.hpp");
+ + m_cppFileNames.constFirst().baseName() + "_XXXXXX.hpp"_L1;
QTemporaryFile ppFile(pattern);
bool autoRemove = !qEnvironmentVariableIsSet("KEEP_TEMP_FILES");
// make sure that a tempfile can be written
result.m_globalEnums = d->m_builder->globalEnums();
result.m_enums = d->m_builder->typeEntryToEnumsHash();
result.m_flags = flags;
+ result.m_typedefTargetToName = d->m_builder->typedefTargetToName();
qSwap(result.m_instantiatedContainers, collectContext.instantiatedContainers);
qSwap(result.m_instantiatedSmartPointers, collectContext.instantiatedSmartPointers);
return result;
AbstractMetaClassPtr ApiExtractor::inheritTemplateClass(const ComplexTypeEntryPtr &te,
const AbstractMetaClassCPtr &templateClass,
- const AbstractMetaTypeList &templateTypes,
- InheritTemplateFlags flags)
+ const AbstractMetaTypeList &templateTypes)
{
return AbstractMetaBuilder::inheritTemplateClass(te, templateClass,
- templateTypes, flags);
+ templateTypes);
}
QString ApiExtractorPrivate::getSimplifiedContainerTypeName(const AbstractMetaType &type)
return;
}
if (type.hasTemplateChildren()) {
- QString piece = isContainer ? QStringLiteral("container") : QStringLiteral("smart pointer");
+ const auto piece = isContainer ? "container"_L1 : "smart pointer"_L1;
QString warning =
QString::fromLatin1("Skipping instantiation of %1 '%2' because it has template"
" arguments.").arg(piece, type.originalTypeDescription());
if (!contextName.isEmpty())
- warning.append(QStringLiteral(" Calling context: ") + contextName);
+ warning.append(" Calling context: "_L1 + contextName);
qCWarning(lcShiboken).noquote().nospace() << warning;
return;
const auto ste = std::static_pointer_cast<const SmartPointerTypeEntry>(smp.smartPointer->typeEntry());
QString name = ste->getTargetName(smp.type);
auto parentTypeEntry = ste->parent();
- InheritTemplateFlags flags;
auto colonPos = name.lastIndexOf(u"::");
const bool withinNameSpace = colonPos != -1;
if (nameSpaces.isEmpty())
throw Exception(msgNamespaceNotFound(name));
parentTypeEntry = nameSpaces.constFirst();
- } else {
- flags.setFlag(InheritTemplateFlag::SetEnclosingClass);
}
TypedefEntryPtr typedefEntry(new TypedefEntry(name, ste->name(), ste->version(),
parentTypeEntry));
typedefEntry->setTargetLangPackage(ste->targetLangPackage());
auto instantiationEntry = TypeDatabase::initializeTypeDefEntry(typedefEntry, ste);
+ instantiationEntry->setParent(parentTypeEntry);
smp.specialized = ApiExtractor::inheritTemplateClass(instantiationEntry, smp.smartPointer,
- {instantiatedType}, flags);
+ {instantiatedType});
Q_ASSERT(smp.specialized);
- if (withinNameSpace) { // move class to desired namespace
+ if (parentTypeEntry->type() != TypeEntry::TypeSystemType) { // move class to desired namespace
const auto enclClass = AbstractMetaClass::findClass(m_builder->classes(), parentTypeEntry);
Q_ASSERT(enclClass);
auto specialized = std::const_pointer_cast<AbstractMetaClass>(smp.specialized);
return;
for (const auto &func : metaClass->functions())
collectInstantiatedContainersAndSmartPointers(context, func);
+ for (const auto &func : metaClass->userAddedPythonOverrides())
+ collectInstantiatedContainersAndSmartPointers(context, func);
for (const AbstractMetaField &field : metaClass->fields())
addInstantiatedContainersAndSmartPointers(context, field.type(), field.name());
static AbstractMetaClassPtr
inheritTemplateClass(const ComplexTypeEntryPtr &te,
const AbstractMetaClassCPtr &templateClass,
- const AbstractMetaTypeList &templateTypes,
- InheritTemplateFlags flags = {});
+ const AbstractMetaTypeList &templateTypes);
private:
ApiExtractorPrivate *d;
Q_DECLARE_FLAGS(ApiExtractorFlags, ApiExtractorFlag)
Q_DECLARE_OPERATORS_FOR_FLAGS(ApiExtractorFlags)
-enum class InheritTemplateFlag
-{
- SetEnclosingClass = 0x1
-};
-
-Q_DECLARE_FLAGS(InheritTemplateFlags, InheritTemplateFlag)
-Q_DECLARE_OPERATORS_FOR_FLAGS(InheritTemplateFlags)
-
#endif // APIEXTRACTORFLAGS_H
#include "enumtypeentry.h"
#include "flagstypeentry.h"
+#include "smartpointertypeentry.h"
ApiExtractorResult::ApiExtractorResult() = default;
return m_instantiatedSmartPointers;
}
+std::optional<InstantiatedSmartPointer>
+ ApiExtractorResult::findSmartPointerInstantiation(const SmartPointerTypeEntryCPtr &pointer,
+ const TypeEntryCPtr &pointee) const
+{
+ for (const auto &smp : m_instantiatedSmartPointers) {
+ const auto &i = smp.type;
+ if (i.typeEntry() == pointer && i.instantiations().at(0).typeEntry() == pointee)
+ return smp;
+ }
+ return std::nullopt;
+}
+
+const QMultiHash<QString, QString> &ApiExtractorResult::typedefTargetToName() const
+{
+ return m_typedefTargetToName;
+}
+
ApiExtractorFlags ApiExtractorResult::flags() const
{
return m_flags;
#include "typesystem_typedefs.h"
#include <QtCore/QHash>
+#include <QtCore/QMultiHash>
#include <optional>
const AbstractMetaTypeList &instantiatedContainers() const;
const InstantiatedSmartPointers &instantiatedSmartPointers() const;
+ std::optional<InstantiatedSmartPointer>
+ findSmartPointerInstantiation(const SmartPointerTypeEntryCPtr &pointer,
+ const TypeEntryCPtr &pointee) const;
+
+ const QMultiHash<QString, QString> &typedefTargetToName() const;
// Query functions for the generators
std::optional<AbstractMetaEnum>
AbstractMetaTypeList m_instantiatedContainers;
InstantiatedSmartPointers m_instantiatedSmartPointers;
QHash<TypeEntryCPtr, AbstractMetaEnum> m_enums;
+ QMultiHash<QString, QString> m_typedefTargetToName;
ApiExtractorFlags m_flags;
friend class ApiExtractor;
namespace clang {
-static inline QString templateBrackets() { return QStringLiteral("<>"); }
-
static inline bool isClassCursor(const CXCursor &c)
{
return c.kind == CXCursor_ClassDecl || c.kind == CXCursor_StructDecl
const qsizetype length = qsizetype(truncate ? newLine : snippetV.size());
QString snippet = QString::fromUtf8(snippetV.data(), length);
if (truncate)
- snippet += QStringLiteral("...");
+ snippet += "..."_L1;
return u"Cannot determine exception specification: \""_s + snippet + u'"';
}
case CXAvailability_Available:
break;
case CXAvailability_Deprecated:
- result->setDeprecated(true);
+ result->setAttribute(FunctionAttribute::Deprecated);
break;
case CXAvailability_NotAvailable: // "Foo(const Foo&) = delete;"
result->setDeleted(true);
auto result = createFunction(cursor, functionType, isTemplateCode);
result->setAccessPolicy(accessPolicy(clang_getCXXAccessSpecifier(cursor)));
result->setConstant(clang_CXXMethod_isConst(cursor) != 0);
- result->setStatic(clang_CXXMethod_isStatic(cursor) != 0);
- result->setVirtual(clang_CXXMethod_isVirtual(cursor) != 0);
- result->setAbstract(clang_CXXMethod_isPureVirtual(cursor) != 0);
+ result->setAttribute(FunctionAttribute::Static, clang_CXXMethod_isStatic(cursor) != 0);
+ result->setAttribute(FunctionAttribute::Virtual, clang_CXXMethod_isVirtual(cursor) != 0);
+ result->setAttribute(FunctionAttribute::Abstract, clang_CXXMethod_isPureVirtual(cursor) != 0);
return result;
}
&& m_currentFunction->arguments().size() == 1
&& clang_CXXConstructor_isCopyConstructor(cursor) == 0
&& clang_CXXConstructor_isMoveConstructor(cursor) == 0) {
- m_currentFunction->setExplicit(clang_CXXConstructor_isConvertingConstructor(cursor) == 0);
+ m_currentFunction->setAttribute(FunctionAttribute::Explicit,
+ clang_CXXConstructor_isConvertingConstructor(cursor) == 0);
}
}
field->setScope(m_scope);
field->setType(createTypeInfo(cursor));
field->setMutable(clang_CXXField_isMutable(cursor) != 0);
+ setFileName(cursor, field.get());
m_currentField = field;
m_scopeStack.back()->addVariable(field);
}
|| !d->addClass(cursor, CodeModel::Class)) {
return Skip;
}
- d->m_currentClass->setName(d->m_currentClass->name() + templateBrackets());
- d->m_scope.back() += templateBrackets();
+ d->m_currentClass->setName(d->m_currentClass->name() + "<>"_L1);
+ d->m_scope.back() += "<>"_L1;
break;
case CXCursor_EnumDecl: {
QString name = enumType(cursor);
EnumKind kind = CEnum;
if (name.isEmpty()) {
kind = AnonymousEnum;
- name = QStringLiteral("enum_") + QString::number(++d->m_anonymousEnumCount);
+ name = "enum_"_L1 + QString::number(++d->m_anonymousEnumCount);
#if !CLANG_NO_ENUMDECL_ISSCOPED
} else if (clang_EnumDecl_isScoped(cursor) != 0) {
#else
}
}
d->m_currentFunction = d->createFunction(cursor, CodeModel::Normal, true);
+ d->setFileName(cursor, d->m_currentFunction.get());
d->m_scopeStack.back()->addFunction(d->m_currentFunction);
break;
case CXCursor_FunctionDecl:
const QString &tplParmName = tItem->name();
if (Q_UNLIKELY(!insertTemplateParameterIntoClassName(tplParmName, d->m_currentClass)
|| !insertTemplateParameterIntoClassName(tplParmName, &d->m_scope.back()))) {
- const QString message = QStringLiteral("Error inserting template parameter \"") + tplParmName
- + QStringLiteral("\" into ") + d->m_currentClass->name();
+ const QString message = "Error inserting template parameter \""_L1 + tplParmName
+ + "\" into "_L1 + d->m_currentClass->name();
const Diagnostic d(message, cursor, CXDiagnostic_Error);
qWarning() << d;
appendDiagnostic(d);
break;
case CXCursor_CXXFinalAttr:
if (d->m_currentFunction)
- d->m_currentFunction->setFinal(true);
+ d->m_currentFunction->setAttribute(FunctionAttribute::Final);
else if (d->m_currentClass)
d->m_currentClass->setFinal(true);
break;
case CXCursor_CXXOverrideAttr:
if (d->m_currentFunction)
- d->m_currentFunction->setOverride(true);
+ d->m_currentFunction->setAttribute(FunctionAttribute::Override);
break;
case CXCursor_StaticAssert:
// Check for Q_PROPERTY() (see PySide6/global.h.in for an explanation
#ifndef CLANGDEBUGUTILS_H
#define CLANGDEBUGUTILS_H
-#include <QtCore/QtGlobal>
+#include <QtCore/qtclasshelpermacros.h>
#include <clang-c/Index.h>
#include <QtCore/QScopedArrayPointer>
#include <QtCore/QString>
+using namespace Qt::StringLiterals;
+
namespace clang {
QString SourceFileCache::getFileName(CXFile file)
if (range.first.file != range.second.file) {
if (errorMessage)
- *errorMessage = QStringLiteral("Range spans several files");
+ *errorMessage = "Range spans several files"_L1;
return std::string_view(empty, 0);
}
const QString fileName = getFileName(range.first.file);
if (fileName.isEmpty()) {
if (errorMessage)
- *errorMessage = QStringLiteral("Range has no file");
+ *errorMessage = "Range has no file"_L1;
return std::string_view(empty, 0);
}
QFile file(fileName);
namespace clang {
-bool SourceLocation::equals(const SourceLocation &rhs) const
-{
- return file == rhs.file && offset == rhs.offset;
-}
-
SourceLocation getExpansionLocation(const CXSourceLocation &location)
{
SourceLocation result;
#include <clang-c/Index.h>
#include <QtCore/QString>
#include <QtCore/QStringList>
+#include <QtCore/QtCompare>
#include <QtCore/QList>
#include <functional>
unsigned line = 0;
unsigned column = 0;
unsigned offset = 0;
-};
-
-inline bool operator==(const SourceLocation &l1, const SourceLocation &l2)
-{ return l1.equals(l2); }
-inline bool operator!=(const SourceLocation &l1, const SourceLocation &l2)
-{ return !l1.equals(l2); }
+ friend constexpr bool comparesEqual(const SourceLocation &lhs,
+ const SourceLocation &rhs) noexcept
+ {
+ return lhs.file == rhs.file && lhs.offset == rhs.offset;
+ }
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(SourceLocation)
+};
SourceLocation getExpansionLocation(const CXSourceLocation &location);
return result;
}
+// 3/2024: Use a recent MSVC2022 for libclang 18.X
+static QByteArray msvcCompatVersion()
+{
+ return libClangVersion() >= QVersionNumber(0, 64) ? "19.39"_ba : "19.26"_ba;
+}
+
static bool runProcess(const QString &program, const QStringList &arguments,
QByteArray *stdOutIn = nullptr, QByteArray *stdErrIn = nullptr)
{
QProcess process;
process.start(program, arguments, QProcess::ReadWrite);
if (!process.waitForStarted()) {
- qWarning().noquote().nospace() << "Unable to start "
+ qCWarning(lcShiboken).noquote().nospace() << "Unable to start "
<< process.program() << ": " << process.errorString();
return false;
}
*stdOutIn = process.readAllStandardOutput();
if (!finished) {
- qWarning().noquote().nospace() << process.program() << " timed out: " << stdErr;
+ qCWarning(lcShiboken).noquote().nospace() << process.program() << " timed out: " << stdErr;
process.kill();
return false;
}
if (process.exitStatus() != QProcess::NormalExit) {
- qWarning().noquote().nospace() << process.program() << " crashed: " << stdErr;
+ qCWarning(lcShiboken).noquote().nospace() << process.program() << " crashed: " << stdErr;
return false;
}
if (process.exitCode() != 0) {
- qWarning().noquote().nospace() << process.program() << " exited "
+ qCWarning(lcShiboken).noquote().nospace() << process.program() << " exited "
<< process.exitCode() << ": " << stdErr;
return false;
}
return {};
const QString path = QFile::decodeName(stdOut.trimmed());
if (!QFileInfo::exists(path)) {
- qWarning(R"(%s: "%s" as returned by llvm-config "%s" does not exist.)",
- __FUNCTION__, qPrintable(QDir::toNativeSeparators(path)), qPrintable(arg));
+ qCWarning(lcShiboken, R"(%s: "%s" as returned by llvm-config "%s" does not exist.)",
+ __FUNCTION__, qPrintable(QDir::toNativeSeparators(path)), qPrintable(arg));
return {};
}
return path;
const QString path = QFile::decodeName(qgetenv(envVar)) + u"/lib"_s;
if (QFileInfo::exists(path))
return path;
- qWarning("%s: %s as pointed to by %s does not exist.", __FUNCTION__, qPrintable(path), envVar);
+ qCWarning(lcShiboken, "%s: %s as pointed to by %s does not exist.",
+ __FUNCTION__, qPrintable(path), envVar);
}
}
return queryLlvmConfigDir(u"--libdir"_s);
const QString clangPathLibDir = findClangLibDir();
if (!clangPathLibDir.isEmpty()) {
QString candidate;
+ QString clangDirName = clangPathLibDir + u"/clang"_s;
+ // PYSIDE-2769: llvm-config --libdir may report /usr/lib64 on manylinux_2_28_x86_64
+ // whereas the includes are under /usr/lib/clang/../include.
+ if (!QFileInfo::exists(clangDirName) && clangPathLibDir.endsWith("64"_L1)) {
+ const QString fallback = clangPathLibDir.sliced(0, clangPathLibDir.size() - 2);
+ clangDirName = fallback + u"/clang"_s;
+ qCWarning(lcShiboken, "%s: Falling back from %s to %s.",
+ __FUNCTION__, qPrintable(clangPathLibDir), qPrintable(fallback));
+ }
+
QVersionNumber lastVersionNumber(1, 0, 0);
- const QString clangDirName = clangPathLibDir + u"/clang"_s;
QDir clangDir(clangDirName);
const QFileInfoList versionDirs =
clangDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
if (versionDirs.isEmpty())
- qWarning("%s: No subdirectories found in %s.", __FUNCTION__, qPrintable(clangDirName));
+ qCWarning(lcShiboken, "%s: No subdirectories found in %s.",
+ __FUNCTION__, qPrintable(clangDirName));
for (const QFileInfo &fi : versionDirs) {
const QString fileName = fi.fileName();
if (fileName.at(0).isDigit()) {
}
}
if (!candidate.isEmpty())
- return candidate + QStringLiteral("/include");
+ return candidate + "/include"_L1;
}
return queryLlvmConfigDir(u"--includedir"_s);
}
+QString compilerFromCMake()
+{
+#ifdef CMAKE_CXX_COMPILER
+ return QString::fromLocal8Bit(CMAKE_CXX_COMPILER);
+#else
+ return {};
+#endif
+}
+
+// Return a compiler suitable for determining the internal include paths
static QString compilerFromCMake(const QString &defaultCompiler)
{
if (!compilerPath().isEmpty())
return compilerPath();
-// Added !defined(Q_OS_DARWIN) due to PYSIDE-1032
- QString result = defaultCompiler;
-#ifdef CMAKE_CXX_COMPILER
- if (platform() != Platform::macOS)
- result = QString::fromLocal8Bit(CMAKE_CXX_COMPILER);
-#endif
- return result;
+ // Exclude macOS since cmakeCompiler returns the full path instead of the
+ // /usr/bin/clang shim, which results in the default SDK sysroot path
+ // missing (PYSIDE-1032)
+ if (platform() == Platform::macOS)
+ return defaultCompiler;
+ QString cmakeCompiler = compilerFromCMake();
+ if (cmakeCompiler.isEmpty())
+ return defaultCompiler;
+ QFileInfo fi(cmakeCompiler);
+ // Should be absolute by default, but a user may specify -DCMAKE_CXX_COMPILER=cl.exe
+ if (fi.isRelative())
+ return cmakeCompiler;
+ if (fi.exists())
+ return fi.absoluteFilePath();
+ // The compiler may not exist in case something like icecream or
+ // a non-standard-path was used on the build machine. Check
+ // the executable.
+ cmakeCompiler = QStandardPaths::findExecutable(fi.fileName());
+ return cmakeCompiler.isEmpty() ? defaultCompiler : cmakeCompiler;
}
static void appendClangBuiltinIncludes(HeaderPaths *p)
HeaderPaths headerPaths;
switch (compiler()) {
case Compiler::Msvc:
- result.append(QByteArrayLiteral("-fms-compatibility-version=19.26.28806"));
+ result.append("-fms-compatibility-version="_ba + msvcCompatVersion());
result.append(QByteArrayLiteral("-fdelayed-template-parsing"));
result.append(QByteArrayLiteral("-Wno-microsoft-enum-value"));
+ result.append("/Zc:__cplusplus"_ba);
// Fix yvals_core.h: STL1000: Unexpected compiler version, expected Clang 7 or newer (MSVC2017 update)
result.append(QByteArrayLiteral("-D_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH"));
if (needsClangBuiltinIncludes())
Compiler compiler();
bool setCompiler(const QString &name);
+QString compilerFromCMake();
+
const QString &compilerPath();
void setCompilerPath(const QString &name);
return -1;
}
-enum class WebXmlTag
+enum class WebXmlCodeTag
{
- Class, Description, Enum, Function, Parameter, Property, Typedef, Other
+ Class, Description, Enum, Function, Header, Parameter, Property, Typedef, Other
};
-static WebXmlTag tag(QStringView name)
+static WebXmlCodeTag tag(QStringView name)
{
if (name == u"class" || name == u"namespace")
- return WebXmlTag::Class;
+ return WebXmlCodeTag::Class;
if (name == u"enum")
- return WebXmlTag::Enum;
+ return WebXmlCodeTag::Enum;
if (name == u"function")
- return WebXmlTag::Function;
+ return WebXmlCodeTag::Function;
if (name == u"description")
- return WebXmlTag::Description;
+ return WebXmlCodeTag::Description;
+ if (name == u"header")
+ return WebXmlCodeTag::Header;
if (name == u"parameter")
- return WebXmlTag::Parameter;
+ return WebXmlCodeTag::Parameter;
if (name == u"property")
- return WebXmlTag::Property;
+ return WebXmlCodeTag::Property;
if (name == u"typedef")
- return WebXmlTag::Typedef;
- return WebXmlTag::Other;
+ return WebXmlCodeTag::Typedef;
+ return WebXmlCodeTag::Other;
}
-static void parseWebXmlElement(WebXmlTag tag, const QXmlStreamAttributes &attributes,
+static void parseWebXmlElement(WebXmlCodeTag tag, const QXmlStreamAttributes &attributes,
ClassDocumentation *cd)
{
switch (tag) {
- case WebXmlTag::Class:
+ case WebXmlCodeTag::Class:
cd->name = attributes.value(u"name"_s).toString();
+ cd->type = ClassDocumentation::Class;
break;
- case WebXmlTag::Enum: {
+ case WebXmlCodeTag::Header:
+ cd->name = attributes.value(u"name"_s).toString();
+ cd->type = ClassDocumentation::Header;
+ break;
+ case WebXmlCodeTag::Enum: {
EnumDocumentation ed;
ed.name = attributes.value(u"name"_s).toString();
cd->enums.append(ed);
}
break;
- case WebXmlTag::Function: {
+ case WebXmlCodeTag::Function: {
FunctionDocumentation fd;
fd.name = attributes.value(u"name"_s).toString();
fd.signature = attributes.value(u"signature"_s).toString();
cd->functions.append(fd);
}
break;
- case WebXmlTag::Parameter:
+ case WebXmlCodeTag::Parameter:
Q_ASSERT(!cd->functions.isEmpty());
cd->functions.last().parameters.append(attributes.value(u"type"_s).toString());
break;
- case WebXmlTag::Property: {
+ case WebXmlCodeTag::Property: {
PropertyDocumentation pd;
pd.name = attributes.value(u"name"_s).toString();
pd.brief = attributes.value(u"brief"_s).toString();
return result;
}
-ClassDocumentation parseWebXml(const QString &fileName, QString *errorMessage)
+std::optional<ClassDocumentation> parseWebXml(const QString &fileName, QString *errorMessage)
{
ClassDocumentation result;
QFile file(fileName);
if (!file.open(QIODevice::Text | QIODevice::ReadOnly)) {
*errorMessage = msgCannotOpenForReading(file);
- return result;
+ return std::nullopt;
}
- WebXmlTag lastTag = WebXmlTag::Other;
+ WebXmlCodeTag lastTag = WebXmlCodeTag::Other;
QXmlStreamReader reader(&file);
while (!reader.atEnd()) {
switch (reader.readNext()) {
const auto currentTag = tag(reader.name());
parseWebXmlElement(currentTag, reader.attributes(), &result);
switch (currentTag) { // Store relevant tags in lastTag
- case WebXmlTag::Class:
- case WebXmlTag::Function:
- case WebXmlTag::Enum:
- case WebXmlTag::Property:
- case WebXmlTag::Typedef:
+ case WebXmlCodeTag::Class:
+ case WebXmlCodeTag::Function:
+ case WebXmlCodeTag::Enum:
+ case WebXmlCodeTag::Header:
+ case WebXmlCodeTag::Property:
+ case WebXmlCodeTag::Typedef:
lastTag = currentTag;
break;
- case WebXmlTag::Description: { // Append the description to the element
+ case WebXmlCodeTag::Description: { // Append the description to the element
QString *target = nullptr;
switch (lastTag) {
- case WebXmlTag::Class:
+ case WebXmlCodeTag::Class:
target = &result.description;
break;
- case WebXmlTag::Function:
+ case WebXmlCodeTag::Function:
target = &result.functions.last().description;
break;
- case WebXmlTag::Enum:
+ case WebXmlCodeTag::Enum:
target = &result.enums.last().description;
break;
- case WebXmlTag::Property:
+ case WebXmlCodeTag::Property:
target = &result.properties.last().description;
default:
break;
if (reader.error() != QXmlStreamReader::NoError) {
*errorMessage= msgXmlError(fileName, reader);
- return {};
+ return std::nullopt;
}
sortDocumentation(&result);
QDebugStateSaver saver(debug);
debug.noquote();
debug.nospace();
- debug << "Class(";
- if (c) {
- debug << c.name << ", ";
- formatDescription(debug, c.description);
- formatList(debug, ", enums", c.enums);
- formatList(debug, ", properties", c.properties);
- formatList(debug, ", functions", c.functions);
- } else {
- debug << "invalid";
- }
+ debug << "Class(" << c.name << ", ";
+ formatDescription(debug, c.description);
+ formatList(debug, ", enums", c.enums);
+ formatList(debug, ", properties", c.properties);
+ formatList(debug, ", functions", c.functions);
debug << ')';
return debug;
}
#include <QtCore/QStringList>
+#include <optional>
+
QT_FORWARD_DECLARE_CLASS(QDebug)
/// An enumeration in a WebXML/doxygen document
using FunctionDocumentationList = QList<FunctionDocumentation>;
-/// A class/namespace in a WebXML/doxygen document
+/// A WebXML/doxygen document
struct ClassDocumentation
{
+ enum Type {
+ Class, // <class>, class/namespace
+ Header // <header>, grouped global functions/enums
+ };
+
qsizetype indexOfEnum(const QString &name) const;
FunctionDocumentationList findFunctionCandidates(const QString &name,
bool constant) const;
const FunctionDocumentationQuery &q);
qsizetype indexOfProperty(const QString &name) const;
+ Type type = Type::Class;
QString name;
QString description;
QList<EnumDocumentation> enums;
QList<PropertyDocumentation> properties;
FunctionDocumentationList functions;
-
- operator bool() const { return !name.isEmpty(); }
};
/// Parse a WebXML class/namespace document
-ClassDocumentation parseWebXml(const QString &fileName, QString *errorMessage);
+std::optional<ClassDocumentation> parseWebXml(const QString &fileName, QString *errorMessage);
/// Extract the module description from a WebXML module document
QString webXmlModuleDescription(const QString &fileName, QString *errorMessage);
QRegularExpression CodeSnipAbstract::placeHolderRegex(int index)
{
- return QRegularExpression(u'%' + QString::number(index) + QStringLiteral("\\b"));
+ return QRegularExpression(u'%' + QString::number(index) + "\\b"_L1);
}
void purgeEmptyCodeSnips(QList<CodeSnip> *list)
ForceAbstract = 0x8,
// Indicates that the instances are used to create hierarchies
// like widgets; parent ownership heuristics are enabled for them.
- ParentManagement = 0x10
+ ParentManagement = 0x10,
+ DisableQtMetaObjectFunctions = 0x20,
+ Typedef = 0x40 // Result of a <typedef-type>
};
Q_DECLARE_FLAGS(TypeFlags, TypeFlag)
{
QStringList result;
#if defined (Q_OS_UNIX)
- result << QStringLiteral("unix");
+ result << "unix"_L1;
#endif
#if defined (Q_OS_LINUX)
- result << QStringLiteral("linux");
+ result << "linux"_L1;
#elif defined (Q_OS_MACOS)
- result << QStringLiteral("darwin");
+ result << "darwin"_L1;
#elif defined (Q_OS_WINDOWS)
- result << QStringLiteral("windows");
+ result << "windows"_L1;
#endif
return result;
}
using namespace Qt::StringLiterals;
-DocParser::DocParser()
+static inline bool isXpathDocModification(const DocModification &mod)
{
-#ifdef HAVE_LIBXSLT
- xmlSubstituteEntitiesDefault(1);
-#endif
+ return mod.mode() == TypeSystem::DocModificationXPathReplace;
}
+static inline bool isNotXpathDocModification(const DocModification &mod)
+{
+ return mod.mode() != TypeSystem::DocModificationXPathReplace;
+}
+
+static void removeXpathDocModifications(DocModificationList *l)
+{
+ l->erase(std::remove_if(l->begin(), l->end(), isXpathDocModification), l->end());
+}
+
+static void removeNonXpathDocModifications(DocModificationList *l)
+{
+ l->erase(std::remove_if(l->begin(), l->end(), isNotXpathDocModification), l->end());
+}
+
+DocParser::DocParser() = default;
DocParser::~DocParser() = default;
+void DocParser::fillGlobalFunctionDocumentation(const AbstractMetaFunctionPtr &)
+{
+}
+
+void DocParser::fillGlobalEnumDocumentation(AbstractMetaEnum &)
+{
+}
+
QString DocParser::getDocumentation(const XQueryPtr &xquery, const QString& query,
const DocModificationList& mods)
{
usesRValueReference);
}
-DocModificationList DocParser::getDocModifications(const AbstractMetaClassCPtr &cppClass,
- const AbstractMetaFunctionCPtr &func)
+DocModificationList DocParser::getDocModifications(const AbstractMetaClassCPtr &cppClass)
+
+{
+ auto result = cppClass->typeEntry()->docModifications();
+ removeXpathDocModifications(&result);
+ return result;
+}
+
+static void filterBySignature(const AbstractMetaFunctionCPtr &func, DocModificationList *l)
{
- auto te = cppClass->typeEntry();
- if (!func)
- return te->docModifications();
+ if (!l->isEmpty()) {
+ const QString minimalSignature = func->minimalSignature();
+ const auto filter = [&minimalSignature](const DocModification &mod) {
+ return mod.signature() != minimalSignature;
+ };
+ l->erase(std::remove_if(l->begin(), l->end(), filter), l->end());
+ }
+}
- if (func->isUserAdded())
- return func->addedFunctionDocModifications();
+DocModificationList DocParser::getDocModifications(const AbstractMetaFunctionCPtr &func,
+ const AbstractMetaClassCPtr &cppClass)
+{
+ DocModificationList result;
+ if (func->isUserAdded()) {
+ result = func->addedFunctionDocModifications();
+ removeXpathDocModifications(&result);
+ } else if (cppClass != nullptr) {
+ result = cppClass->typeEntry()->functionDocModifications();
+ removeXpathDocModifications(&result);
+ filterBySignature(func, &result);
+ }
+ return result;
+}
- DocModificationList result = te->functionDocModifications();
- const QString minimalSignature = func->minimalSignature();
- const auto filter = [&minimalSignature](const DocModification &mod) {
- return mod.signature() != minimalSignature;
- };
- result.erase(std::remove_if(result.begin(), result.end(), filter), result.end());
+DocModificationList DocParser::getXpathDocModifications(const AbstractMetaClassCPtr &cppClass)
+{
+ auto result = cppClass->typeEntry()->docModifications();
+ removeNonXpathDocModifications(&result);
+ return result;
+}
+
+DocModificationList DocParser::getXpathDocModifications(const AbstractMetaFunctionCPtr &func,
+ const AbstractMetaClassCPtr &cppClass)
+{
+ DocModificationList result;
+ if (func->isUserAdded()) {
+ result = func->addedFunctionDocModifications();
+ removeNonXpathDocModifications(&result);
+ } else if (cppClass != nullptr) {
+ result = cppClass->typeEntry()->functionDocModifications();
+ removeNonXpathDocModifications(&result);
+ filterBySignature(func, &result);
+ }
return result;
}
return result;
}
-static inline bool isXpathDocModification(const DocModification &mod)
-{
- return mod.mode() == TypeSystem::DocModificationXPathReplace;
-}
-
-QString DocParser::applyDocModifications(const DocModificationList& mods, const QString& xml)
+QString DocParser::applyDocModifications(const DocModificationList& xpathMods,
+ const QString& xml)
{
const char xslPrefix[] =
R"(<xsl:template match="/">
</xsl:template>
)";
- if (mods.isEmpty() || xml.isEmpty()
- || !std::any_of(mods.cbegin(), mods.cend(), isXpathDocModification)) {
+ if (xpathMods.isEmpty() || xml.isEmpty())
return xml;
- }
QString xsl = QLatin1StringView(xslPrefix);
- for (const DocModification &mod : mods) {
- if (isXpathDocModification(mod)) {
- QString xpath = mod.xpath();
- xpath.replace(u'"', u"""_s);
- xsl += u"<xsl:template match=\""_s
- + xpath + u"\">"_s
- + mod.code() + u"</xsl:template>\n"_s;
- }
+ for (const DocModification &mod : xpathMods) {
+ Q_ASSERT(isXpathDocModification(mod));
+ QString xpath = mod.xpath();
+ xpath.replace(u'"', u"""_s);
+ xsl += "<xsl:template match=\""_L1 + xpath + "\">"_L1
+ + mod.code() + "</xsl:template>\n"_L1;
}
QString errorMessage;
const QString result = xsl_transform(xml, xsl, &errorMessage);
if (!errorMessage.isEmpty())
qCWarning(lcShibokenDoc, "%s",
- qPrintable(msgXpathDocModificationError(mods, errorMessage)));
+ qPrintable(msgXpathDocModificationError(xpathMods, errorMessage)));
if (result == xml) {
const QString message = u"Query did not result in any modifications to \""_s
+ xml + u'"';
qCWarning(lcShibokenDoc, "%s",
- qPrintable(msgXpathDocModificationError(mods, message)));
+ qPrintable(msgXpathDocModificationError(xpathMods, message)));
}
return result;
}
DocParser();
virtual ~DocParser();
virtual void fillDocumentation(const AbstractMetaClassPtr &metaClass) = 0;
+ virtual void fillGlobalFunctionDocumentation(const AbstractMetaFunctionPtr &f);
+ virtual void fillGlobalEnumDocumentation(AbstractMetaEnum &e);
/**
* Process and retrieves documentation concerning the entire
/// Helper to return the documentation modifications for a class
/// or a member function.
- static DocModificationList getDocModifications(const AbstractMetaClassCPtr &cppClass,
- const AbstractMetaFunctionCPtr &func = {});
+ static DocModificationList getDocModifications(const AbstractMetaClassCPtr &cppClass);
+ static DocModificationList getDocModifications(const AbstractMetaFunctionCPtr &func,
+ const AbstractMetaClassCPtr &cppClass = {});
+ static DocModificationList getXpathDocModifications(const AbstractMetaClassCPtr &cppClass);
+ static DocModificationList getXpathDocModifications(const AbstractMetaFunctionCPtr &func,
+ const AbstractMetaClassCPtr &cppClass = {});
static QString enumBaseClass(const AbstractMetaEnum &e);
static AbstractMetaFunctionCList documentableFunctions(const AbstractMetaClassCPtr &metaClass);
- static QString applyDocModifications(const DocModificationList &mods, const QString &xml);
+ static QString applyDocModifications(const DocModificationList &xpathMods, const QString &xml);
private:
QString m_packageName;
m_format = f;
}
-bool Documentation::equals(const Documentation &rhs) const
-{
- return m_format == rhs.m_format && m_detailed == rhs.m_detailed
- && m_brief == rhs.m_brief;
-}
-
void Documentation::setDetailed(const QString &detailed)
{
m_detailed = detailed.trimmed();
#define DOCUMENTATION_H
#include <QtCore/QString>
+#include <QtCore/QtCompare>
QT_FORWARD_DECLARE_CLASS(QDebug)
void setBrief(const QString &brief);
private:
+ friend bool comparesEqual(const Documentation &lhs,
+ const Documentation &rhs) noexcept
+ {
+ return lhs.m_format == rhs.m_format && lhs.m_detailed == rhs.m_detailed
+ && lhs.m_brief == rhs.m_brief;
+ }
+ Q_DECLARE_EQUALITY_COMPARABLE(Documentation)
+
QString m_detailed;
QString m_brief;
Format m_format = Documentation::Native;
};
-inline bool operator==(const Documentation &d1, const Documentation &d2)
-{ return d1.equals(d2); }
-inline bool operator!=(const Documentation &d1, const Documentation &d2)
-{ return !d1.equals(d2); }
-
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug debug, const Documentation &);
#endif
bool showDotGraph(const QString &name, const QString &graph)
{
- const QString imageType = u"jpg"_s;
+ constexpr auto imageType = "jpg"_L1;
// Write out the graph to a temporary file
QTemporaryFile dotFile(QDir::tempPath() + u'/' + name + u"_XXXXXX.dot"_s);
// Launch image. Should use QDesktopServices::openUrl(),
// but we don't link against QtGui
#ifdef Q_OS_UNIX
- const QString imageViewer = u"gwenview"_s;
+ constexpr auto imageViewer = "gwenview"_L1;
#else
- const QString imageViewer = u"mspaint"_s;
+ constexpr auto imageViewer = "mspaint"_L1;
#endif
if (!QProcess::startDetached(imageViewer, {imageFile})) {
qWarning("Failed to launch viewer: %s", qPrintable(imageViewer));
doxyFileSuffix += metaClass->name();
doxyFileSuffix += u".xml"_s;
- const char* prefixes[] = { "class", "struct", "namespace" };
+ static constexpr QLatin1StringView prefixes[] = { "class"_L1, "struct"_L1, "namespace"_L1 };
bool isProperty = false;
QString doxyFilePath;
- for (const char *prefix : prefixes) {
- doxyFilePath = documentationDataDirectory() + u'/'
- + QLatin1StringView(prefix) + doxyFileSuffix;
+ for (const auto &prefix : prefixes) {
+ doxyFilePath = documentationDataDirectory() + u'/' + prefix + doxyFileSuffix;
if (QFile::exists(doxyFilePath))
break;
doxyFilePath.clear();
}
QString doc = getDocumentation(xquery, funcQuery,
- DocParser::getDocModifications(metaClass, func));
+ DocParser::getXpathDocModifications(func, metaClass));
if (doc.isEmpty()) {
qCWarning(lcShibokenDoc, "%s",
qPrintable(msgCannotFindDocumentation(doxyFilePath, func.get(),
void addEnumValueRejection(const QString &name);
QStringList enumValueRejections() const;
+ QString docFile() const;
+ void setDocFile(const QString &df);
+
TypeEntry *clone() const override;
#ifndef QT_NO_DEBUG_STREAM
void formatDebug(QDebug &d) const override;
if (!FileOut::m_dryRun) {
QDir dir(info.absolutePath());
if (!dir.mkpath(dir.absolutePath())) {
- const QString message = QStringLiteral("Unable to create directory '%1'")
+ const QString message = QString::fromLatin1("Unable to create directory '%1'")
.arg(QDir::toNativeSeparators(dir.absolutePath()));
throw Exception(message);
}
bool hasSignature(const QString& signature) const;
void addSignature(const QString& signature);
+ QString docFile() const;
+ void setDocFile(const QString &df);
+
TypeEntry *clone() const override;
#ifndef QT_NO_DEBUG_STREAM
return u"import "_s + m_name + u';';
}
-int Include::compare(const Include &rhs) const
+Qt::strong_ordering compareThreeWay(const Include &lhs, const Include &rhs) noexcept
{
- if (m_type < rhs.m_type)
- return -1;
- if (m_type > rhs.m_type)
- return 1;
- return m_name.compare(rhs.m_name);
+ if (lhs.m_type < rhs.m_type)
+ return Qt::strong_ordering::less;
+ if (lhs.m_type > rhs.m_type)
+ return Qt::strong_ordering::greater;
+ if (auto c = lhs.m_name.compare(rhs.m_name))
+ return c < 0 ? Qt::strong_ordering::less : Qt::strong_ordering::greater;
+ return Qt::strong_ordering::equal;
}
QTextStream& operator<<(QTextStream& out, const Include& g)
#ifndef INCLUDE_H
#define INCLUDE_H
+#include <QtCore/QtCompare>
#include <QtCore/QHashFunctions>
#include <QtCore/QString>
#include <QtCore/QList>
{
return qHashMulti(seed, inc.m_type, inc.m_name);
}
+ friend bool comparesEqual(const Include &lhs, const Include &rhs) noexcept
+ {
+ return lhs.m_type == rhs.m_type && lhs.m_name == rhs.m_name;
+ }
+ friend Qt::strong_ordering compareThreeWay(const Include &lhs,
+ const Include &rhs) noexcept;
+ Q_DECLARE_STRONGLY_ORDERED(Include)
IncludeType m_type = IncludePath;
QString m_name;
};
-inline bool operator<(const Include &lhs, const Include &rhs)
-{
- return lhs.compare(rhs) < 0;
-}
-
-inline bool operator<=(const Include &lhs, const Include &rhs)
-{
- return lhs.compare(rhs) <= 0;
-}
-
-inline bool operator==(const Include &lhs, const Include &rhs)
-{
- return lhs.compare(rhs) == 0;
-}
-
-inline bool operator!=(const Include &lhs, const Include &rhs)
-{
- return lhs.compare(rhs) != 0;
-}
-
-inline bool operator>=(const Include &lhs, const Include &rhs)
-{
- return lhs.compare(rhs) >= 0;
-}
-
-inline bool operator>(const Include &lhs, const Include &rhs)
-{
- return lhs.compare(rhs) > 0;
-}
-
QTextStream& operator<<(QTextStream& out, const Include& include);
TextStream& operator<<(TextStream& out, const Include& include);
#ifndef QT_NO_DEBUG_STREAM
// abstractmetabuilder.cpp
+static QTextStream &operator<<(QTextStream &s, Access a)
+{
+ switch (a) {
+ case Access::Public:
+ s << "public";
+ break;
+ case Access::Protected:
+ s << "protected";
+ break;
+ case Access::Private:
+ s << "private";
+ break;
+ }
+ return s;
+}
+
QString msgNoFunctionForModification(const AbstractMetaClassCPtr &klass,
const QString &signature,
const QString &originalSignature,
{
QString result;
QTextStream str(&result);
- str << functionItem->sourceLocation() << "skipping ";
- if (functionItem->isAbstract())
+ str << functionItem->sourceLocation() << "skipping "
+ << functionItem->accessPolicy() << ' ';
+ const bool isAbstract = functionItem->attributes().testFlag(FunctionAttribute::Abstract);
+ if (isAbstract)
str << "abstract ";
str << "function '" << signature << "', " << why;
- if (functionItem->isAbstract()) {
+ if (isAbstract) {
str << "\nThis will lead to compilation errors due to not "
"being able to instantiate the wrapper.";
}
{
QString result;
QTextStream str(&result);
- str << field->sourceLocation() << "skipping field '" << className
- << "::" << field->name() << "' with unmatched type '" << type << '\'';
+ str << field->sourceLocation() << "skipping " << field->accessPolicy()
+ << " field '" << className << "::" << field->name()
+ << "' with unmatched type '" << type << '\'';
return result;
}
+ u"\" for instantiation of \""_s +smartPointerType + u"\"."_s;
}
+QString msgInheritTemplateIssue(const AbstractMetaClassPtr &subclass,
+ const TypeInfo &info,
+ const QString &what)
+{
+ return "While inheriting template "_L1 + subclass->name()
+ + " from "_L1 + info.toString() + ": "_L1 + what;
+}
+
+QString msgIgnoringTemplateParameter(const QString &typeName,
+ const char *why)
+{
+ return "Ignoring template parameter "_L1 + typeName +
+ ": "_L1 + QLatin1StringView(why);
+}
+
QString msgInvalidSmartPointerType(const TypeInfo &i)
{
return u"Invalid smart pointer type \""_s +i.toString() + u"\"."_s;
const AbstractMetaEnum &e,
const QString &query)
{
- return msgCannotFindDocumentation(fileName, "enum",
- metaClass->name() + u"::"_s + e.name(),
- query);
+ QString name = e.name();
+ if (metaClass != nullptr)
+ name.prepend(metaClass->name() + "::"_L1);
+ return msgCannotFindDocumentation(fileName, "enum", name, query);
}
QString msgCannotFindDocumentation(const QString &fileName,
const AbstractMetaField &f,
const QString &query)
{
- return msgCannotFindDocumentation(fileName, "field",
- metaClass->name() + u"::"_s + f.name(),
- query);
+ QString name = f.name();
+ if (metaClass != nullptr)
+ name.prepend(metaClass->name() + "::"_L1);
+ return msgCannotFindDocumentation(fileName, "field", name, query);
}
QString msgXpathDocModificationError(const DocModificationList& mods,
QString msgCannotOpenForReading(const QFile &f)
{
- return QStringLiteral("Failed to open file '%1' for reading: %2")
+ return QString::fromLatin1("Failed to open file '%1' for reading: %2")
.arg(QDir::toNativeSeparators(f.fileName()), f.errorString());
}
QString msgCannotOpenForWriting(const QFile &f)
{
- return QStringLiteral("Failed to open file '%1' for writing: %2")
+ return QString::fromLatin1("Failed to open file '%1' for writing: %2")
.arg(QDir::toNativeSeparators(f.fileName()), f.errorString());
}
QString msgCannotFindTypeEntry(const QString &t);
QString msgCannotFindTypeEntryForSmartPointer(const QString &t, const QString &smartPointerType);
+QString msgInheritTemplateIssue(const AbstractMetaClassPtr &subclass,
+ const TypeInfo &info, const QString &what);
+QString msgIgnoringTemplateParameter(const QString &typeName,
+ const char *why);
QString msgInvalidSmartPointerType(const TypeInfo &i);
QString msgCannotFindSmartPointerInstantion(const TypeInfo &i);
using namespace Qt::StringLiterals;
-// ---------------------- Modification
-QString FunctionModification::accessModifierString() const
-{
- if (isPrivate())
- return u"private"_s;
- if (isProtected())
- return u"protected"_s;
- if (isPublic())
- return u"public"_s;
- if (isFriendly())
- return u"friendly"_s;
- return {};
-}
-
// ---------------------- FieldModification
class FieldModificationData : public QSharedData
enum ModifierFlag {
Private = 0x0001,
Protected = 0x0002,
- Public = 0x0003,
- Friendly = 0x0004,
+ Public = 0x0004,
AccessModifierMask = 0x000f,
Final = 0x0010,
{
return accessModifier() == Public;
}
- bool isFriendly() const
- {
- return accessModifier() == Friendly;
- }
bool isFinal() const
{
return modifiers().testFlag(Final);
{
return modifiers().testFlag(NonFinal);
}
- QString accessModifierString() const;
bool isDeprecated() const
{
#include <QtCore/QRegularExpression>
#include <algorithm>
-#include <functional>
-#include <iostream>
using namespace Qt::StringLiterals;
-// Predicate to find an item by name in a list of std::shared_ptr<Item>
-template <class T> class ModelItemNamePredicate
-{
-public:
- explicit ModelItemNamePredicate(const QString &name) : m_name(name) {}
- bool operator()(const std::shared_ptr<T> &item) const { return item->name() == m_name; }
-
-private:
- const QString m_name;
-};
-
template <class T>
-static std::shared_ptr<T> findModelItem(const QList<std::shared_ptr<T> > &list, const QString &name)
+static std::shared_ptr<T> findModelItem(const QList<std::shared_ptr<T> > &list,
+ QAnyStringView name)
{
- const auto it = std::find_if(list.cbegin(), list.cend(), ModelItemNamePredicate<T>(name));
- return it != list.cend() ? *it : std::shared_ptr<T>();
+ using ItemPtr = std::shared_ptr<T>;
+ auto pred = [name](const ItemPtr &item) { return item->name() == name; };
+ const auto it = std::find_if(list.cbegin(), list.cend(), pred);
+ return it != list.cend() ? *it : ItemPtr{};
}
// ---------------------------------------------------------------------------
m_files.append(item);
}
-FileModelItem CodeModel::findFile(const QString &name) const
+FileModelItem CodeModel::findFile(QAnyStringView name) const
{
return findModelItem(m_files, name);
}
}
#endif // !QT_NO_DEBUG_STREAM
-namespace {
// Predicate to match a non-template class name against the class list.
// "Vector" should match "Vector" as well as "Vector<T>" (as seen for methods
// from within the class "Vector").
-class ClassNamePredicate
+static bool matchClassNameNonTemplatePart(const ClassModelItem &item, const QString &name)
{
-public:
- explicit ClassNamePredicate(const QString &name) : m_name(name) {}
- bool operator()(const ClassModelItem &item) const
- {
- const QString &itemName = item->name();
- if (!itemName.startsWith(m_name))
- return false;
- return itemName.size() == m_name.size() || itemName.at(m_name.size()) == u'<';
- }
-
-private:
- const QString m_name;
-};
-} // namespace
+ const QString &itemName = item->name();
+ if (!itemName.startsWith(name))
+ return false;
+ return itemName.size() == name.size() || itemName.at(name.size()) == u'<';
+}
ClassModelItem _ScopeModelItem::findClass(const QString &name) const
{
// A fully qualified template is matched by name only
const ClassList::const_iterator it = name.contains(u'<')
- ? std::find_if(m_classes.begin(), m_classes.end(), ModelItemNamePredicate<_ClassModelItem>(name))
- : std::find_if(m_classes.begin(), m_classes.end(), ClassNamePredicate(name));
+ ? std::find_if(m_classes.begin(), m_classes.end(),
+ [&name](const ClassModelItem &item) {
+ return item->name() == name; })
+ : std::find_if(m_classes.begin(), m_classes.end(),
+ [&name](const ClassModelItem &item) {
+ return matchClassNameNonTemplatePart(item, name); });
return it != m_classes.end() ? *it : ClassModelItem();
}
-VariableModelItem _ScopeModelItem::findVariable(const QString &name) const
+VariableModelItem _ScopeModelItem::findVariable(QAnyStringView name) const
{
return findModelItem(m_variables, name);
}
-TypeDefModelItem _ScopeModelItem::findTypeDef(const QString &name) const
+TypeDefModelItem _ScopeModelItem::findTypeDef(QAnyStringView name) const
{
return findModelItem(m_typeDefs, name);
}
-TemplateTypeAliasModelItem _ScopeModelItem::findTemplateTypeAlias(const QString &name) const
+TemplateTypeAliasModelItem _ScopeModelItem::findTemplateTypeAlias(QAnyStringView name) const
{
return findModelItem(m_templateTypeAliases, name);
}
-EnumModelItem _ScopeModelItem::findEnum(const QString &name) const
+EnumModelItem _ScopeModelItem::findEnum(QAnyStringView name) const
{
return findModelItem(m_enums, name);
}
return findEnumByValueRecursion(this, value, enumValue);
}
-FunctionList _ScopeModelItem::findFunctions(const QString &name) const
+FunctionList _ScopeModelItem::findFunctions(QAnyStringView name) const
{
FunctionList result;
for (const FunctionModelItem &func : m_functions) {
m_namespaces.append(item);
}
-NamespaceModelItem _NamespaceModelItem::findNamespace(const QString &name) const
+NamespaceModelItem _NamespaceModelItem::findNamespace(QAnyStringView name) const
{
return findModelItem(m_namespaces, name);
}
m_isDeleted = d;
}
-bool _FunctionModelItem::isDeprecated() const
-{
- return m_isDeprecated;
-}
-
-void _FunctionModelItem::setDeprecated(bool d)
-{
- m_isDeprecated = d;
-}
-
-bool _FunctionModelItem::isVirtual() const
-{
- return m_isVirtual;
-}
-
-void _FunctionModelItem::setVirtual(bool isVirtual)
-{
- m_isVirtual = isVirtual;
-}
-
bool _FunctionModelItem::isInline() const
{
return m_isInline;
}
-bool _FunctionModelItem::isOverride() const
-{
- return m_isOverride;
-}
-
-void _FunctionModelItem::setOverride(bool o)
-{
- m_isOverride = o;
-}
-
-bool _FunctionModelItem::isFinal() const
-{
- return m_isFinal;
-}
-
-void _FunctionModelItem::setFinal(bool f)
-{
- m_isFinal = f;
-}
-
void _FunctionModelItem::setInline(bool isInline)
{
m_isInline = isInline;
}
-bool _FunctionModelItem::isExplicit() const
-{
- return m_isExplicit;
-}
-
-void _FunctionModelItem::setExplicit(bool isExplicit)
-{
- m_isExplicit = isExplicit;
-}
-
bool _FunctionModelItem::isHiddenFriend() const
{
return m_isHiddenFriend;
m_isHiddenFriend = f;
}
-bool _FunctionModelItem::isAbstract() const
-{
- return m_isAbstract;
-}
-
-void _FunctionModelItem::setAbstract(bool isAbstract)
-{
- m_isAbstract = isAbstract;
-}
-
-// Qt
-bool _FunctionModelItem::isInvokable() const
-{
- return m_isInvokable;
-}
-
-void _FunctionModelItem::setInvokable(bool isInvokable)
-{
- m_isInvokable = isInvokable;
-}
-
QString _FunctionModelItem::typeSystemSignature() const // For dumping out type system files
{
QString result;
d << " [deleted!]";
if (m_isInline)
d << " [inline]";
- if (m_isVirtual)
+ if (m_attributes.testFlag(FunctionAttribute::Virtual))
d << " [virtual]";
- if (m_isOverride)
+ if (m_attributes.testFlag(FunctionAttribute::Override))
d << " [override]";
- if (m_isDeprecated)
+ if (m_attributes.testFlag(FunctionAttribute::Deprecated))
d << " [deprecated]";
- if (m_isFinal)
+ if (m_attributes.testFlag(FunctionAttribute::Final))
d << " [final]";
- if (m_isAbstract)
+ if (m_attributes.testFlag(FunctionAttribute::Abstract))
d << " [abstract]";
- if (m_isExplicit)
+ if (m_attributes.testFlag(FunctionAttribute::Explicit))
d << " [explicit]";
if (m_isInvokable)
d << " [invokable]";
NamespaceModelItem globalNamespace() const;
void addFile(const FileModelItem &item);
- FileModelItem findFile(const QString &name) const;
+ FileModelItem findFile(QAnyStringView name) const;
static CodeModelItem findItem(const QStringList &qualifiedName,
const ScopeModelItem &scope);
void addVariable(const VariableModelItem &item);
ClassModelItem findClass(const QString &name) const;
- EnumModelItem findEnum(const QString &name) const;
+ EnumModelItem findEnum(QAnyStringView name) const;
struct FindEnumByValueReturn
{
};
FindEnumByValueReturn findEnumByValue(QStringView value) const;
- FunctionList findFunctions(const QString &name) const;
- TypeDefModelItem findTypeDef(const QString &name) const;
- TemplateTypeAliasModelItem findTemplateTypeAlias(const QString &name) const;
- VariableModelItem findVariable(const QString &name) const;
+ FunctionList findFunctions(QAnyStringView name) const;
+ TypeDefModelItem findTypeDef(QAnyStringView name) const;
+ TemplateTypeAliasModelItem findTemplateTypeAlias(QAnyStringView name) const;
+ VariableModelItem findVariable(QAnyStringView name) const;
void addEnumsDeclaration(const QString &enumsDeclaration);
QStringList enumsDeclarations() const { return m_enumsDeclarations; }
void addNamespace(NamespaceModelItem item);
- NamespaceModelItem findNamespace(const QString &name) const;
+ NamespaceModelItem findNamespace(QAnyStringView name) const;
void appendNamespace(const _NamespaceModelItem &other);
static std::optional<CodeModel::FunctionType> functionTypeFromName(QStringView name);
+ FunctionAttributes attributes() const { return m_attributes; }
+ void setAttributes(FunctionAttributes a) { m_attributes = a; }
+ void setAttribute(FunctionAttribute a, bool on = true) { m_attributes.setFlag(a, on); }
+
bool isDeleted() const;
void setDeleted(bool d);
- bool isDeprecated() const;
- void setDeprecated(bool d);
-
- bool isVirtual() const;
- void setVirtual(bool isVirtual);
-
- bool isOverride() const;
- void setOverride(bool o);
-
- bool isFinal() const;
- void setFinal(bool f);
-
bool isInline() const;
void setInline(bool isInline);
- bool isExplicit() const;
- void setExplicit(bool isExplicit);
-
bool isHiddenFriend() const;
void setHiddenFriend(bool f);
- bool isInvokable() const; // Qt
- void setInvokable(bool isInvokable); // Qt
-
- bool isAbstract() const;
- void setAbstract(bool isAbstract);
-
bool isVariadics() const;
void setVariadics(bool isVariadics);
CodeModel::FunctionType _determineTypeHelper() const;
ArgumentList m_arguments;
+ FunctionAttributes m_attributes;
CodeModel::FunctionType m_functionType = CodeModel::Normal;
union {
struct {
uint m_isDeleted: 1;
- uint m_isVirtual: 1;
- uint m_isOverride: 1;
- uint m_isFinal: 1;
- uint m_isDeprecated: 1;
uint m_isInline: 1;
- uint m_isAbstract: 1;
- uint m_isExplicit: 1;
uint m_isVariadics: 1;
uint m_isHiddenFriend: 1;
uint m_isInvokable : 1; // Qt
#ifndef CODEMODEL_ENUMS_H
#define CODEMODEL_ENUMS_H
+#include <QtCore/qflags.h>
+
enum ReferenceType {
NoReference,
LValueReference,
Public
};
+enum class FunctionAttribute {
+ Abstract = 0x00000001,
+ Static = 0x00000002,
+ Virtual = 0x00000004,
+ Override = 0x00000008,
+ Final = 0x00000010,
+ Deprecated = 0x00000020, // Code annotation
+ Explicit = 0x00000040, // Constructor
+};
+
+Q_DECLARE_FLAGS(FunctionAttributes, FunctionAttribute)
+Q_DECLARE_OPERATORS_FOR_FLAGS(FunctionAttributes)
+
#endif // CODEMODEL_ENUMS_H
return result;
}
-bool EnumValue::equals(const EnumValue &rhs) const
+bool comparesEqual(const EnumValue &lhs, const EnumValue &rhs) noexcept
{
- if (m_type != rhs.m_type)
+ if (lhs.m_type != rhs.m_type)
return false;
- return m_type == Signed ? m_value == rhs.m_value : m_unsignedValue == rhs.m_unsignedValue;
+ return lhs.m_type == EnumValue::Signed
+ ? lhs.m_value == rhs.m_value : lhs.m_unsignedValue == rhs.m_unsignedValue;
}
void EnumValue::formatDebugHex(QDebug &d) const
#ifndef ENUMVALUE_H
#define ENUMVALUE_H
-#include <QtCore/QtGlobal>
+#include <QtCore/qtypes.h>
+#include <QtCore/qtclasshelpermacros.h>
+#include <QtCore/QtCompare>
QT_FORWARD_DECLARE_CLASS(QDebug)
QT_FORWARD_DECLARE_CLASS(QString)
void formatDebugHex(QDebug &d) const;
private:
+ friend bool comparesEqual(const EnumValue &lhs,
+ const EnumValue &rhs) noexcept;
+ Q_DECLARE_EQUALITY_COMPARABLE(EnumValue)
+
#ifndef QT_NO_DEBUG_STREAM
friend QDebug operator<<(QDebug, const EnumValue &);
#endif
Type m_type = Signed;
};
-inline bool operator==(const EnumValue &e1, const EnumValue &e2)
-{ return e1.equals(e2); }
-inline bool operator!=(const EnumValue &e1, const EnumValue &e2)
-{ return !e1.equals(e2); }
-
#endif // ENUMVALUE_H
d->m_instantiations.clear();
}
+bool TypeInfo::isPlain() const
+{
+ return d->m_constant == 0 && d->m_volatile == 0 && d->m_referenceType == NoReference
+ && d->m_indirections.isEmpty() && d->m_arrayElements.isEmpty();
+}
+
TypeInfo TypeInfo::resolveType(TypeInfo const &__type, const ScopeModelItem &__scope)
{
CodeModel *__model = __scope->model();
&& m_instantiations == other.m_instantiations;
}
-bool TypeInfo::equals(const TypeInfo &other) const
+
+bool comparesEqual(const TypeInfo &lhs, const TypeInfo &rhs) noexcept
{
- return d.data() == other.d.data() || d->equals(*other.d);
+ return lhs.d.data() == rhs.d.data() || lhs.d->equals(*rhs.d);
}
QString TypeInfo::indirectionKeyword(Indirection i)
{
- return i == Indirection::Pointer
- ? QStringLiteral("*") : QStringLiteral("*const");
+ return i == Indirection::Pointer ? "*"_L1 : "*const"_L1;
}
-static inline QString constQualifier() { return QStringLiteral("const"); }
-static inline QString volatileQualifier() { return QStringLiteral("volatile"); }
-
bool TypeInfo::stripLeadingConst(QString *s)
{
- return stripLeadingQualifier(constQualifier(), s);
+ return stripLeadingQualifier("const"_L1, s);
}
bool TypeInfo::stripLeadingVolatile(QString *s)
{
- return stripLeadingQualifier(volatileQualifier(), s);
+ return stripLeadingQualifier("volatile"_L1, s);
}
-bool TypeInfo::stripLeadingQualifier(const QString &qualifier, QString *s)
+bool TypeInfo::stripLeadingQualifier(QLatin1StringView qualifier, QString *s)
{
// "const int x"
const auto qualifierSize = qualifier.size();
#include <QtCore/QString>
#include <QtCore/QSharedDataPointer>
+#include <QtCore/QtCompare>
#include <QtCore/QStringList>
#include <utility>
void addInstantiation(const TypeInfo &i);
void clearInstantiations();
+ bool isPlain() const; // neither const,volatile, no indirections/references, array
+
bool isStdType() const;
std::pair<qsizetype, qsizetype>
parseTemplateArgumentList(const QString &l, qsizetype from = 0);
- bool equals(const TypeInfo &other) const;
-
// ### arrays and templates??
QString toString() const;
static bool stripLeadingConst(QString *s);
static bool stripLeadingVolatile(QString *s);
- static bool stripLeadingQualifier(const QString &qualifier, QString *s);
+ static bool stripLeadingQualifier(QLatin1StringView qualifier, QString *s);
static void stripQualifiers(QString *s);
void simplifyStdType();
private:
+ friend bool comparesEqual(const TypeInfo &lhs,
+ const TypeInfo &rhs) noexcept;
+ Q_DECLARE_EQUALITY_COMPARABLE(TypeInfo)
+
QSharedDataPointer<TypeInfoData> d;
friend class TypeInfoTemplateArgumentHandler;
static TypeInfo resolveType(CodeModelItem item, TypeInfo const &__type, const ScopeModelItem &__scope);
};
-inline bool operator==(const TypeInfo &t1, const TypeInfo &t2)
-{ return t1.equals(t2); }
-
-inline bool operator!=(const TypeInfo &t1, const TypeInfo &t2)
-{ return !t1.equals(t2); }
-
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug d, const TypeInfo &t);
#endif
)"_s;
}
-static const char stlMapKeyAccessor[] = "->first";
-static const char stlMapValueAccessor[] = "->second";
-static const char qtMapKeyAccessor[] = ".key()";
-static const char qtMapValueAccessor[] = ".value()";
+static constexpr auto stlMapKeyAccessor = "->first"_L1;
+static constexpr auto stlMapValueAccessor = "->second"_L1;
+static constexpr auto qtMapKeyAccessor = ".key()"_L1;
+static constexpr auto qtMapValueAccessor = ".value()"_L1;
static QString cppMapToPyDict(bool isQMap)
{
return uR"(PyObject *%out = PyDict_New();
for (auto it = std::cbegin(%in), end = std::cend(%in); it != end; ++it) {
const auto &key = it)"_s
- + QLatin1StringView(isQMap ? qtMapKeyAccessor : stlMapKeyAccessor)
+ + (isQMap ? qtMapKeyAccessor : stlMapKeyAccessor)
+ uR"(;
const auto &value = it)"_s
- + QLatin1StringView(isQMap ? qtMapValueAccessor : stlMapValueAccessor)
+ + (isQMap ? qtMapValueAccessor : stlMapValueAccessor)
+ uR"(;
PyObject *pyKey = %CONVERTTOPYTHON[%INTYPE_0](key);
PyObject *pyValue = %CONVERTTOPYTHON[%INTYPE_1](value);
return uR"(PyObject *%out = PyDict_New();
for (auto it = std::cbegin(%in), end = std::cend(%in); it != end; ) {
const auto &key = it)"_s
- + QLatin1StringView(isQMultiMap ? qtMapKeyAccessor : stlMapKeyAccessor)
+ + (isQMultiMap ? qtMapKeyAccessor : stlMapKeyAccessor)
+ uR"(;
PyObject *pyKey = %CONVERTTOPYTHON[%INTYPE_0](key);
auto upper = %in.)"_s
return uR"(PyObject *%out = PyDict_New();
for (auto it = std::cbegin(%in), end = std::cend(%in); it != end; ) {
const auto &key = it)"_s
- + QLatin1StringView(isQMultiHash ? qtMapKeyAccessor : stlMapKeyAccessor)
+ + (isQMultiHash ? qtMapKeyAccessor : stlMapKeyAccessor)
+ uR"(;
PyObject *pyKey = %CONVERTTOPYTHON[%INTYPE_0](key);
auto range = %in.equal_range(key);
enum class PropertyToken { None, Read, Write, Designable, Reset, Notify };
static const QHash<QString, PropertyToken> tokenLookup = {
- {QStringLiteral("READ"), PropertyToken::Read},
- {QStringLiteral("WRITE"), PropertyToken::Write},
- {QStringLiteral("DESIGNABLE"), PropertyToken::Designable},
- {QStringLiteral("RESET"), PropertyToken::Reset},
- {QStringLiteral("NOTIFY"), PropertyToken::Notify}
+ {"READ"_L1, PropertyToken::Read},
+ {"WRITE"_L1, PropertyToken::Write},
+ {"DESIGNABLE"_L1, PropertyToken::Designable},
+ {"RESET"_L1, PropertyToken::Reset},
+ {"NOTIFY"_L1, PropertyToken::Notify}
};
errorMessage->clear();
struct castToPyCFunction
{
- explicit castToPyCFunction(QStringView function) noexcept :
+ explicit castToPyCFunction(QAnyStringView function) noexcept :
m_function(function) {}
- QStringView m_function;
+ QAnyStringView m_function;
};
struct PyMethodDefEntry
#ifndef QTCOMPAT_H
#define QTCOMPAT_H
-#include <QtCore/QtGlobal>
+#include <QtCore/qtconfigmacros.h>
#if QT_VERSION < 0x060400
#include "reporthandler.h"
#include "flagstypeentry.h"
#include "complextypeentry.h"
+#include "functiontypeentry.h"
+#include "enumtypeentry.h"
#include "qtcompat.h"
enum { debugFunctionSearch = 0 };
-static inline QString briefStartElement() { return QStringLiteral("<brief>"); }
-static inline QString briefEndElement() { return QStringLiteral("</brief>"); }
+constexpr auto briefStartElement = "<brief>"_L1;
+constexpr auto briefEndElement = "</brief>"_L1;
+constexpr auto webxmlSuffix = ".webxml"_L1;
Documentation QtDocParser::retrieveModuleDocumentation()
{
queryFunctionDocumentation(sourceFileName, classDocumentation, metaClass,
func, errorMessage);
- const auto funcModifs = DocParser::getDocModifications(metaClass, func);
+ const auto funcModifs = DocParser::getXpathDocModifications(func, metaClass);
return docString.isEmpty() || funcModifs.isEmpty()
? docString : applyDocModifications(funcModifs, docString);
}
// from the source.
static QString extractBrief(QString *value)
{
- const auto briefStart = value->indexOf(briefStartElement());
+ const auto briefStart = value->indexOf(briefStartElement);
if (briefStart < 0)
return {};
- const auto briefEnd = value->indexOf(briefEndElement(),
- briefStart + briefStartElement().size());
+ const auto briefEnd = value->indexOf(briefEndElement,
+ briefStart + briefStartElement.size());
if (briefEnd < briefStart)
return {};
- const auto briefLength = briefEnd + briefEndElement().size() - briefStart;
+ const auto briefLength = briefEnd + briefEndElement.size() - briefStart;
QString briefValue = value->mid(briefStart, briefLength);
- briefValue.insert(briefValue.size() - briefEndElement().size(),
+ briefValue.insert(briefValue.size() - briefEndElement.size(),
u"<rst> More_...</rst>"_s);
value->remove(briefStart, briefLength);
return briefValue;
}
+// Find the webxml file for global functions/enums
+// by the doc-file typesystem attribute or via include file.
+static QString findGlobalWebXmLFile(const QString &documentationDataDirectory,
+ const QString &docFile,
+ const Include &include)
+{
+ QString result;
+ if (!docFile.isEmpty()) {
+ result = documentationDataDirectory + u'/' + docFile;
+ if (!result.endsWith(webxmlSuffix))
+ result += webxmlSuffix;
+ return QFileInfo::exists(result) ? result : QString{};
+ }
+ if (include.name().isEmpty())
+ return {};
+ // qdoc "\headerfile <QtLogging>" directive produces "qtlogging.webxml"
+ result = documentationDataDirectory + u'/' +
+ QFileInfo(include.name()).baseName() + webxmlSuffix;
+ if (QFileInfo::exists(result))
+ return result;
+ // qdoc "\headerfile <qdrawutil.h>" produces "qdrawutil-h.webxml"
+ result.insert(result.size() - webxmlSuffix.size(), "-h"_L1);
+ return QFileInfo::exists(result) ? result : QString{};
+}
+
+void QtDocParser::fillGlobalFunctionDocumentation(const AbstractMetaFunctionPtr &f)
+{
+ auto te = f->typeEntry();
+ if (te == nullptr)
+ return;
+
+ const QString sourceFileName =
+ findGlobalWebXmLFile(documentationDataDirectory(), te->docFile(), te->include());
+ if (sourceFileName.isEmpty())
+ return;
+
+ QString errorMessage;
+ auto classDocumentationO = parseWebXml(sourceFileName, &errorMessage);
+ if (!classDocumentationO.has_value()) {
+ qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
+ return;
+ }
+ const QString detailed =
+ functionDocumentation(sourceFileName, classDocumentationO.value(),
+ {}, f, &errorMessage);
+ if (!errorMessage.isEmpty())
+ qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
+ const Documentation documentation(detailed, {});
+ f->setDocumentation(documentation);
+}
+
+void QtDocParser::fillGlobalEnumDocumentation(AbstractMetaEnum &e)
+{
+ auto te = e.typeEntry();
+ const QString sourceFileName =
+ findGlobalWebXmLFile(documentationDataDirectory(), te->docFile(), te->include());
+ if (sourceFileName.isEmpty())
+ return;
+
+ QString errorMessage;
+ auto classDocumentationO = parseWebXml(sourceFileName, &errorMessage);
+ if (!classDocumentationO.has_value()) {
+ qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
+ return;
+ }
+ if (!extractEnumDocumentation(classDocumentationO.value(), e)) {
+ qCWarning(lcShibokenDoc, "%s",
+ qPrintable(msgCannotFindDocumentation(sourceFileName, {}, e, {})));
+ }
+}
+
void QtDocParser::fillDocumentation(const AbstractMetaClassPtr &metaClass)
{
if (!metaClass)
+ metaClass->qualifiedCppName().toLower();
sourceFileRoot.replace(u"::"_s, u"-"_s);
- QFileInfo sourceFile(sourceFileRoot + QStringLiteral(".webxml"));
+ QFileInfo sourceFile(sourceFileRoot + webxmlSuffix);
if (!sourceFile.exists())
- sourceFile.setFile(sourceFileRoot + QStringLiteral(".xml"));
+ sourceFile.setFile(sourceFileRoot + ".xml"_L1);
if (!sourceFile.exists()) {
qCWarning(lcShibokenDoc).noquote().nospace()
<< "Can't find qdoc file for class " << metaClass->name() << ", tried: "
const QString sourceFileName = sourceFile.absoluteFilePath();
QString errorMessage;
- const ClassDocumentation classDocumentation = parseWebXml(sourceFileName, &errorMessage);
- if (!classDocumentation) {
+ const auto classDocumentationO = parseWebXml(sourceFileName, &errorMessage);
+ if (!classDocumentationO.has_value()) {
qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
return;
}
+ const auto &classDocumentation = classDocumentationO.value();
for (const auto &p : classDocumentation.properties) {
Documentation doc(p.description, p.brief);
metaClass->setPropertyDocumentation(p.name, doc);
}
- QString docString = applyDocModifications(metaClass->typeEntry()->docModifications(),
+ QString docString = applyDocModifications(DocParser::getXpathDocModifications(metaClass),
classDocumentation.description);
if (docString.isEmpty()) {
#endif
// Enums
for (AbstractMetaEnum &meta_enum : metaClass->enums()) {
- Documentation enumDoc;
- const auto index = classDocumentation.indexOfEnum(meta_enum.name());
- if (index != -1) {
- QString doc = classDocumentation.enums.at(index).description;
- const auto firstPara = doc.indexOf(u"<para>");
- if (firstPara != -1) {
- const QString baseClass = QtDocParser::enumBaseClass(meta_enum);
- if (baseClass != u"Enum") {
- const QString note = u"(inherits <teletype>enum."_s + baseClass
- + u"</teletype>) "_s;
- doc.insert(firstPara + 6, note);
- }
- }
- enumDoc.setValue(doc);
- meta_enum.setDocumentation(enumDoc);
- } else {
+ if (!extractEnumDocumentation(classDocumentation, meta_enum)) {
qCWarning(lcShibokenDoc, "%s",
qPrintable(msgCannotFindDocumentation(sourceFileName, metaClass, meta_enum, {})));
}
}
}
+bool QtDocParser::extractEnumDocumentation(const ClassDocumentation &classDocumentation,
+ AbstractMetaEnum &meta_enum)
+{
+ Documentation enumDoc;
+ const auto index = classDocumentation.indexOfEnum(meta_enum.name());
+ if (index == -1)
+ return false;
+ QString doc = classDocumentation.enums.at(index).description;
+ const auto firstPara = doc.indexOf(u"<para>");
+ if (firstPara != -1) {
+ const QString baseClass = QtDocParser::enumBaseClass(meta_enum);
+ if (baseClass != "Enum"_L1) {
+ const QString note = "(inherits <teletype>enum."_L1 + baseClass
+ + "</teletype>) "_L1;
+ doc.insert(firstPara + 6, note);
+ }
+ }
+ enumDoc.setValue(doc);
+ meta_enum.setDocumentation(enumDoc);
+ return true;
+}
+
static QString qmlReferenceLink(const QFileInfo &qmlModuleFi)
{
QString result;
public:
QtDocParser() = default;
void fillDocumentation(const AbstractMetaClassPtr &metaClass) override;
+ void fillGlobalFunctionDocumentation(const AbstractMetaFunctionPtr &f) override;
+ void fillGlobalEnumDocumentation(AbstractMetaEnum &e) override;
+
Documentation retrieveModuleDocumentation() override;
Documentation retrieveModuleDocumentation(const QString& name) override;
const AbstractMetaClassCPtr &metaClass,
const AbstractMetaFunctionCPtr &func,
QString *errorMessage);
+ static bool extractEnumDocumentation(const ClassDocumentation &classDocumentation,
+ AbstractMetaEnum &meta_enum);
+
};
#endif // QTDOCPARSER_H
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 4);
- const auto a = AbstractMetaClass::findClass(classes, u"A");
- const auto b = AbstractMetaClass::findClass(classes, u"B");
- const auto c = AbstractMetaClass::findClass(classes, u"C");
- const auto f = AbstractMetaClass::findClass(classes, u"F");
+ const auto a = AbstractMetaClass::findClass(classes, "A");
+ const auto b = AbstractMetaClass::findClass(classes, "B");
+ const auto c = AbstractMetaClass::findClass(classes, "C");
+ const auto f = AbstractMetaClass::findClass(classes, "F");
QVERIFY(f);
QCOMPARE(a->baseClass(), nullptr);
const auto funcF = virtualFunctionsF.constFirst();
QCOMPARE(funcA->ownerClass(), a);
- QVERIFY(funcC->attributes().testFlag(AbstractMetaFunction::VirtualCppMethod));
+ QVERIFY(funcC->isVirtual());
QCOMPARE(funcB->ownerClass(), b);
QCOMPARE(funcC->ownerClass(), c);
- QVERIFY(funcC->attributes().testFlag(AbstractMetaFunction::OverriddenCppMethod));
- QVERIFY(funcF->attributes().testFlag(AbstractMetaFunction::FinalCppMethod));
+ QVERIFY(funcC->cppAttributes().testFlag(FunctionAttribute::Override));
+ QVERIFY(funcF->cppAttributes().testFlag(FunctionAttribute::Final));
QCOMPARE(funcA->declaringClass(), a);
QCOMPARE(funcB->declaringClass(), a);
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto base = AbstractMetaClass::findClass(classes, u"Base");
+ const auto base = AbstractMetaClass::findClass(classes, "Base");
QVERIFY(base);
QVERIFY(base->isPolymorphic());
- const auto derived = AbstractMetaClass::findClass(classes, u"Derived");
+ const auto derived = AbstractMetaClass::findClass(classes, "Derived");
QVERIFY(derived);
QVERIFY(derived->isPolymorphic());
}
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 2);
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
const auto candidates = classA->queryFunctionsByName(u"method"_s);
QCOMPARE(candidates.size(), 1);
const auto &method = candidates.constFirst();
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 2);
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
const auto methodMatches = classA->queryFunctionsByName(u"method"_s);
QCOMPARE(methodMatches.size(), 1);
const auto method = methodMatches.constFirst();
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 2);
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
QVERIFY(classA->isPolymorphic());
- const auto classB = AbstractMetaClass::findClass(classes, u"A::B");
+ const auto classB = AbstractMetaClass::findClass(classes, "A::B");
QVERIFY(classB);
QVERIFY(!classB->isPolymorphic());
}
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 2);
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
- const auto classB = AbstractMetaClass::findClass(classes, u"A::B");
+ const auto classB = AbstractMetaClass::findClass(classes, "A::B");
QVERIFY(classB);
- const auto fooF = classB->findFunction(u"foo");
+ const auto fooF = classB->findFunction("foo");
QVERIFY(fooF);
}
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 2);
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
auto ctors = classA->queryFunctions(FunctionQueryOption::AnyConstructor);
QCOMPARE(ctors.size(), 2);
QCOMPARE(assigmentOps.constFirst()->functionType(),
AbstractMetaFunction::AssignmentOperatorFunction);
- const auto classB = AbstractMetaClass::findClass(classes, u"B");
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
QVERIFY(classB);
ctors = classB->queryFunctions(FunctionQueryOption::AnyConstructor);
QCOMPARE(ctors.size(), 2);
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 6);
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
QCOMPARE(classA->functions().size(), 2);
QCOMPARE(ctors[1]->arguments().size(), 1);
QCOMPARE(ctors[1]->minimalSignature(), u"A(A)");
- const auto classB = AbstractMetaClass::findClass(classes, u"B");
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
QVERIFY(classB);
QCOMPARE(classB->functions().size(), 2);
QCOMPARE(classB->functions().constFirst()->minimalSignature(), u"B()");
- const auto classC = AbstractMetaClass::findClass(classes, u"C");
+ const auto classC = AbstractMetaClass::findClass(classes, "C");
QVERIFY(classC);
QCOMPARE(classC->functions().size(), 1);
QCOMPARE(classC->functions().constFirst()->minimalSignature(), u"C(C)");
- const auto classD = AbstractMetaClass::findClass(classes, u"D");
+ const auto classD = AbstractMetaClass::findClass(classes, "D");
QVERIFY(classD);
QCOMPARE(classD->functions().size(), 1);
QCOMPARE(classD->functions().constFirst()->minimalSignature(), u"D(D)");
QVERIFY(classD->functions().constFirst()->isPrivate());
- const auto classE = AbstractMetaClass::findClass(classes, u"E");
+ const auto classE = AbstractMetaClass::findClass(classes, "E");
QVERIFY(classE);
QVERIFY(classE->hasPrivateDestructor());
QCOMPARE(classE->functions().size(), 0);
- const auto classF = AbstractMetaClass::findClass(classes, u"F");
+ const auto classF = AbstractMetaClass::findClass(classes, "F");
QVERIFY(classF);
ctors = classF->queryFunctions(FunctionQueryOption::AnyConstructor);
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 2);
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
auto ctors = classA->queryFunctions(FunctionQueryOption::AnyConstructor);
QCOMPARE(ctors[1]->minimalSignature(), u"A(A)");
QVERIFY(ctors[1]->isPrivate());
- const auto classB = AbstractMetaClass::findClass(classes, u"B");
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
QVERIFY(classB);
ctors = classB->queryFunctions(FunctionQueryOption::Constructors);
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 1);
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
const auto ctors = classA->queryFunctions(FunctionQueryOption::Constructors);
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 1);
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
const auto ctors = classA->queryFunctions(FunctionQueryOption::Constructors);
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 2);
- const auto b = AbstractMetaClass::findClass(classes, u"A");
+ const auto b = AbstractMetaClass::findClass(classes, "A");
QVERIFY(!b->isPolymorphic());
- const auto a = AbstractMetaClass::findClass(classes, u"B");
+ const auto a = AbstractMetaClass::findClass(classes, "B");
QVERIFY(!a->isPolymorphic());
}
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 2);
- const auto base = AbstractMetaClass::findClass(classes, u"Base");
+ const auto base = AbstractMetaClass::findClass(classes, "Base");
QVERIFY(base);
- const auto derived = AbstractMetaClass::findClass(classes, u"Derived");
+ const auto derived = AbstractMetaClass::findClass(classes, "Derived");
QVERIFY(derived);
QCOMPARE(derived->baseClasses().value(0), base);
}
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 2);
- const auto base = AbstractMetaClass::findClass(classes, u"Base");
+ const auto base = AbstractMetaClass::findClass(classes, "Base");
QVERIFY(base);
- const auto derived = AbstractMetaClass::findClass(classes, u"Derived");
+ const auto derived = AbstractMetaClass::findClass(classes, "Derived");
QVERIFY(derived);
const auto usingMembers = derived->usingMembers();
QCOMPARE(usingMembers.size(), 2);
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(code.constData(), xmlCode));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto valueList = AbstractMetaClass::findClass(classes, u"ValueList");
+ const auto valueList = AbstractMetaClass::findClass(classes, "ValueList");
QVERIFY(valueList);
auto list = valueList->templateBaseClass();
QVERIFY(valueList->isUsingMember(list, u"append"_s, Access::Public));
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto tc = AbstractMetaClass::findClass(classes, u"TestClass");
+ const auto tc = AbstractMetaClass::findClass(classes, "TestClass");
// Verify that the constructor and 2 functions are generated.
const auto &functions = tc->functions();
QCOMPARE(functions.size(), 5);
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 1);
- const auto c = AbstractMetaClass::findClass(classes, u"C");
+ const auto c = AbstractMetaClass::findClass(classes, "C");
QVERIFY(c);
QVERIFY(c->isTypeDef());
}
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 1);
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
const auto overloads = classA->queryFunctionsByName(u"method"_s);
QCOMPARE(overloads.size(), 1);
using namespace Qt::StringLiterals;
+static constexpr auto voidT = "void"_L1;
+
void TestAddFunction::testParsingFuncNameAndConstness()
{
// generic test...
- const char sig1[] = "func(type1, const type2, const type3* const)";
+ static constexpr auto sig1 = "func(type1, const type2, const type3* const)"_L1;
QString errorMessage;
- auto f1 = AddedFunction::createAddedFunction(QLatin1StringView(sig1), u"void"_s,
- &errorMessage);
+ auto f1 = AddedFunction::createAddedFunction(sig1, voidT, &errorMessage);
QVERIFY2(f1, qPrintable(errorMessage));
QCOMPARE(f1->name(), u"func");
QCOMPARE(f1->arguments().size(), 3);
TypeInfo retval = f1->returnType();
- QCOMPARE(retval.qualifiedName(), QStringList{u"void"_s});
+ QCOMPARE(retval.qualifiedName(), QStringList{voidT});
QCOMPARE(retval.indirections(), 0);
QCOMPARE(retval.isConstant(), false);
QCOMPARE(retval.referenceType(), NoReference);
// test with a ugly template as argument and other ugly stuff
- const char sig2[] = " _fu__nc_ ( type1, const type2, const Abc<int& , C<char*> * > * *@my_name@, const type3* const ) const ";
- auto f2 = AddedFunction::createAddedFunction(QLatin1StringView(sig2),
+ static constexpr auto sig2 =
+ " _fu__nc_ ( type1, const type2, const Abc<int& , C<char*> * >"
+ " * *@my_name@, const type3* const ) const "_L1;
+ auto f2 = AddedFunction::createAddedFunction(sig2,
u"const Abc<int& , C<char*> * > * *"_s,
&errorMessage);
QVERIFY2(f2, qPrintable(errorMessage));
QVERIFY(args.at(3).name.isEmpty());
// function with no args.
- const char sig3[] = "func()";
- auto f3 = AddedFunction::createAddedFunction(QLatin1StringView(sig3), u"void"_s,
- &errorMessage);
+ auto f3 = AddedFunction::createAddedFunction("func()"_L1, voidT, &errorMessage);
QVERIFY2(f3, qPrintable(errorMessage));
QCOMPARE(f3->name(), u"func");
QCOMPARE(f3->arguments().size(), 0);
// const call operator
- const char sig4[] = "operator()(int)const";
- auto f4 = AddedFunction::createAddedFunction(QLatin1StringView(sig4), u"int"_s,
- &errorMessage);
+ auto f4 = AddedFunction::createAddedFunction("operator()(int)const"_L1,
+ "int"_L1, &errorMessage);
QVERIFY2(f4, qPrintable(errorMessage));
QCOMPARE(f4->name(), u"operator()");
QCOMPARE(f4->arguments().size(), 1);
QVERIFY(builder);
auto *typeDb = TypeDatabase::instance();
AbstractMetaClassList classes = builder->classes();
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
// default ctor, default copy ctor, func a() and the added functions
QCOMPARE(classA->functions().size(), 5);
- auto addedFunc = classA->findFunction(u"b");
+ auto addedFunc = classA->findFunction("b");
QVERIFY(addedFunc);
QCOMPARE(addedFunc->access(), Access::Protected);
QCOMPARE(addedFunc->functionType(), AbstractMetaFunction::NormalFunction);
QCOMPARE(args.at(1).defaultValueExpression(), u"4.6");
QCOMPARE(args.at(2).type().typeEntry(), typeDb->findType(u"B"_s));
- auto addedCallOperator = classA->findFunction(u"operator()");
+ auto addedCallOperator = classA->findFunction("operator()");
QVERIFY(addedCallOperator);
}
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
QCOMPARE(classA->functions().size(), 3); // default and added ctors
const auto addedFunc = classA->functions().constLast();
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
// default ctor, default copy ctor and the added function
QCOMPARE(classA->functions().size(), 3);
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
const auto addedFunc = classA->functions().constLast();
QVERIFY(addedFunc->hasInjectedCode());
void TestAddFunction::testAddFunctionWithoutParenteses()
{
- const char sig1[] = "func";
+ static constexpr auto sig1 = "func"_L1;
QString errorMessage;
- auto f1 = AddedFunction::createAddedFunction(QLatin1StringView(sig1), u"void"_s,
- &errorMessage);
+ auto f1 = AddedFunction::createAddedFunction(sig1, voidT, &errorMessage);
QVERIFY2(f1, qPrintable(errorMessage));
QCOMPARE(f1->name(), u"func");
QCOMPARE(f1->arguments().size(), 0);
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
- const auto addedFunc = classA->findFunction(u"func");
+ const auto addedFunc = classA->findFunction(sig1);
QVERIFY(addedFunc);
QVERIFY(addedFunc->hasInjectedCode());
const auto snips = addedFunc->injectedCodeSnips(TypeSystem::CodeSnipPositionAny,
void TestAddFunction::testAddFunctionWithDefaultArgs()
{
- const char sig1[] = "func";
+ static constexpr auto sig1 = "func"_L1;
QString errorMessage;
- auto f1 = AddedFunction::createAddedFunction(QLatin1StringView(sig1), u"void"_s,
- &errorMessage);
+ auto f1 = AddedFunction::createAddedFunction(sig1, voidT, &errorMessage);
QVERIFY2(f1, qPrintable(errorMessage));
QCOMPARE(f1->name(), u"func");
QCOMPARE(f1->arguments().size(), 0);
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
- const auto addedFunc = classA->findFunction(u"func");
+ const auto addedFunc = classA->findFunction(sig1);
QVERIFY(addedFunc);
const AbstractMetaArgument &arg = addedFunc->arguments().at(1);
QCOMPARE(arg.defaultValueExpression(), u"2");
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
auto *typeDb = TypeDatabase::instance();
void TestAddFunction::testAddFunctionWithVarargs()
{
- const char sig1[] = "func(int,char,...)";
QString errorMessage;
- auto f1 = AddedFunction::createAddedFunction(QLatin1StringView(sig1), u"void"_s,
+ auto f1 = AddedFunction::createAddedFunction("func(int,char,...)"_L1, voidT,
&errorMessage);
QVERIFY2(f1, qPrintable(errorMessage));
QCOMPARE(f1->name(), u"func");
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
- const auto addedFunc = classA->findFunction(u"func");
+ const auto addedFunc = classA->findFunction("func");
QVERIFY(addedFunc);
const AbstractMetaArgument &arg = addedFunc->arguments().constLast();
QVERIFY(arg.type().isVarargs());
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
- const auto addedFunc = classA->findFunction(u"func");
+ const auto addedFunc = classA->findFunction("func");
QVERIFY(addedFunc);
QVERIFY(addedFunc->isStatic());
}
QVERIFY(builder);
const auto globalFuncs = builder->globalFunctions();
QCOMPARE(globalFuncs.size(), 2);
- const auto classB = AbstractMetaClass::findClass(builder->classes(), u"B");
+ const auto classB = AbstractMetaClass::findClass(builder->classes(), "B");
QVERIFY(classB);
- QVERIFY(!classB->findFunction(u"globalFunc"));
- QVERIFY(!classB->findFunction(u"globalFunc2"));
+ QVERIFY(!classB->findFunction("globalFunc"));
+ QVERIFY(!classB->findFunction("globalFunc2"));
QVERIFY(!globalFuncs[0]->injectedCodeSnips().isEmpty());
QVERIFY(!globalFuncs[1]->injectedCodeSnips().isEmpty());
}
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto foo = AbstractMetaClass::findClass(classes, u"Foo");
- const auto method = foo->findFunction(u"method");
+ const auto foo = AbstractMetaClass::findClass(classes, "Foo");
+ const auto method = foo->findFunction("method");
QVERIFY(method);
QCOMPARE(method->arguments().size(), 2);
const AbstractMetaArgument &arg = method->arguments().at(1);
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto foo = AbstractMetaClass::findClass(classes, u"FooInt");
+ const auto foo = AbstractMetaClass::findClass(classes, "FooInt");
QVERIFY(foo);
QVERIFY(foo->hasNonPrivateConstructor());
const auto &lst = foo->queryFunctions(FunctionQueryOption::AnyConstructor);
for (const auto &f : lst)
QVERIFY(f->signature().startsWith(f->name()));
QCOMPARE(lst.size(), 2);
- const auto method = foo->findFunction(u"method");
+ const auto method = foo->findFunction("method");
QVERIFY(method);
}
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
QVERIFY(!builder.isNull());
- const auto classA = AbstractMetaClass::findClass(builder->classes(), u"A");
+ const auto classA = AbstractMetaClass::findClass(builder->classes(), "A");
QVERIFY(classA);
const AbstractMetaArgument &arg = classA->functions().constLast()->arguments().constFirst();
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
QVERIFY(!builder.isNull());
- const auto classA = AbstractMetaClass::findClass(builder->classes(), u"A");
+ const auto classA = AbstractMetaClass::findClass(builder->classes(), "A");
QCOMPARE(functionMinimalSignature(classA, u"mi1"_s),
u"mi1(int[5])");
QCOMPARE(functionMinimalSignature(classA, u"mi1c"_s),
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
QVERIFY(!builder.isNull());
- AbstractMetaClassPtr classA = AbstractMetaClass::findClass(builder->classes(), u"A");
+ AbstractMetaClassPtr classA = AbstractMetaClass::findClass(builder->classes(), "A");
QVERIFY(classA);
auto someEnum = classA->findEnum(u"SomeEnum"_s);
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
QVERIFY(builder);
- const auto classA = AbstractMetaClass::findClass(builder->classes(), u"A");
+ const auto classA = AbstractMetaClass::findClass(builder->classes(), "A");
QVERIFY(classA);
AbstractMetaEnum someEnum = builder->globalEnums().constFirst();
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode.toLocal8Bit().constData()));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QCOMPARE(classA->typeEntry()->codeSnips().size(), 1);
QString code = classA->typeEntry()->codeSnips().constFirst().code();
QVERIFY(code.indexOf(expected) != -1);
true, u"1.0"_s));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QCOMPARE(classA->typeEntry()->codeSnips().size(), 1);
}
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QCOMPARE(classA->typeEntry()->codeSnips().size(), 0);
}
str << "\n2nd table\n|" << AlignedField("bla", 3, QTextStream::AlignLeft)
<< '|' << AlignedField(QString{}, 0, QTextStream::AlignLeft) << "|\n";
-static const char expected[] = R"(void foo(int a, int b) {
+constexpr auto expected = R"(void foo(int a, int b) {
if (a == b)
return a;
#if Q_OS_WIN
2nd table
|bla||
-)";
+)"_L1;
- QCOMPARE(str.toString(), QLatin1String(expected));
+ QCOMPARE(str.toString(), expected);
}
void TestCodeInjections::testTextStreamRst()
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 2);
//search for class A
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
auto baseContainer = classA->typeEntry()->baseContainerType();
QVERIFY(baseContainer);
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 3);
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
QCOMPARE(classA->templateBaseClassInstantiations().size(), 1);
const AbstractMetaType templateInstanceType =
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
- const auto classB = AbstractMetaClass::findClass(classes, u"B");
- const auto classC = AbstractMetaClass::findClass(classes, u"C");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
+ const auto classC = AbstractMetaClass::findClass(classes, "C");
QVERIFY(classA);
QVERIFY(classB);
QVERIFY(classC);
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
QCOMPARE(classA->externalConversionOperators().size(), 0);
}
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
- const auto classB = AbstractMetaClass::findClass(classes, u"B");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
QVERIFY(classA);
QVERIFY(classB);
QCOMPARE(classA->functions().size(), 2);
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
- const auto classB = AbstractMetaClass::findClass(classes, u"B");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
QVERIFY(classA);
QVERIFY(classB);
QCOMPARE(classA->functions().size(), 2);
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
- const auto classB = AbstractMetaClass::findClass(classes, u"B");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
QVERIFY(classA);
QVERIFY(classB);
QCOMPARE(classA->functions().size(), 2);
{
// FIXME PYSIDE7 remove
// temp file used later
- const char conversionData[] = "Hi! I'm a conversion rule.";
+ constexpr auto conversionData = "Hi! I'm a conversion rule."_L1;
QTemporaryFile file;
- file.open();
- QCOMPARE(file.write(conversionData), qint64(sizeof(conversionData)-1));
+ QVERIFY(file.open());
+ QCOMPARE(file.write(conversionData.constData()), conversionData.size());
file.close();
const char cppCode[] = "struct A {};\n";
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode.toLocal8Bit().data()));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
const auto typeEntry = classA->typeEntry();
QVERIFY(typeEntry->isValue());
auto vte = std::static_pointer_cast<const ValueTypeEntry>(typeEntry);
QVERIFY(vte->hasTargetConversionRule());
- QCOMPARE(vte->targetConversionRule(), QLatin1String(conversionData));
+ QCOMPARE(vte->targetConversionRule(), conversionData);
}
void TestConversionRuleTag::testConversionRuleTagReplace()
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
QVERIFY(builder);
- const auto classA = AbstractMetaClass::findClass(builder->classes(), u"Date");
+ const auto classA = AbstractMetaClass::findClass(builder->classes(), "Date");
QVERIFY(classA);
QVERIFY(classA->typeEntry()->isValue());
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 3);
- auto klass = AbstractMetaClass::findClass(classes, u"Control");
+ auto klass = AbstractMetaClass::findClass(classes, "Control");
QVERIFY(klass);
QVERIFY(klass->hasNonPrivateConstructor());
- klass = AbstractMetaClass::findClass(classes, u"Subject");
+ klass = AbstractMetaClass::findClass(classes, "Subject");
QVERIFY(klass);
QVERIFY(!klass->hasNonPrivateConstructor());
- klass = AbstractMetaClass::findClass(classes, u"CtorLess");
+ klass = AbstractMetaClass::findClass(classes, "CtorLess");
QVERIFY(klass);
QVERIFY(klass->hasNonPrivateConstructor());
}
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 2);
- const auto base = AbstractMetaClass::findClass(classes, u"Base");
+ const auto base = AbstractMetaClass::findClass(classes, "Base");
QCOMPARE(base->hasNonPrivateConstructor(), true);
- const auto derived = AbstractMetaClass::findClass(classes, u"Derived");
+ const auto derived = AbstractMetaClass::findClass(classes, "Derived");
QCOMPARE(derived->hasNonPrivateConstructor(), true);
}
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- QVERIFY(AbstractMetaClass::findClass(classes, u"ValueA"));
- QVERIFY(!AbstractMetaClass::findClass(classes, u"ValueB"));
- QVERIFY(AbstractMetaClass::findClass(classes, u"ObjectA"));
- QVERIFY(!AbstractMetaClass::findClass(classes, u"ObjectB"));
- QVERIFY(AbstractMetaClass::findClass(classes, u"NamespaceA"));
- QVERIFY(!AbstractMetaClass::findClass(classes, u"NamespaceA::InnerClassA"));
- QVERIFY(!AbstractMetaClass::findClass(classes, u"NamespaceB"));
+ QVERIFY(AbstractMetaClass::findClass(classes, "ValueA"));
+ QVERIFY(!AbstractMetaClass::findClass(classes, "ValueB"));
+ QVERIFY(AbstractMetaClass::findClass(classes, "ObjectA"));
+ QVERIFY(!AbstractMetaClass::findClass(classes, "ObjectB"));
+ QVERIFY(AbstractMetaClass::findClass(classes, "NamespaceA"));
+ QVERIFY(!AbstractMetaClass::findClass(classes, "NamespaceA::InnerClassA"));
+ QVERIFY(!AbstractMetaClass::findClass(classes, "NamespaceB"));
AbstractMetaEnumList globalEnums = builder->globalEnums();
QCOMPARE(globalEnums.size(), 1);
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- QVERIFY(AbstractMetaClass::findClass(classes, u"ValueA"));
- QVERIFY(AbstractMetaClass::findClass(classes, u"ValueB"));
- QVERIFY(AbstractMetaClass::findClass(classes, u"ObjectA"));
- QVERIFY(AbstractMetaClass::findClass(classes, u"ObjectB"));
- QVERIFY(AbstractMetaClass::findClass(classes, u"NamespaceA"));
- QVERIFY(AbstractMetaClass::findClass(classes, u"NamespaceA::InnerClassA"));
- QVERIFY(AbstractMetaClass::findClass(classes, u"NamespaceB"));
+ QVERIFY(AbstractMetaClass::findClass(classes, "ValueA"));
+ QVERIFY(AbstractMetaClass::findClass(classes, "ValueB"));
+ QVERIFY(AbstractMetaClass::findClass(classes, "ObjectA"));
+ QVERIFY(AbstractMetaClass::findClass(classes, "ObjectB"));
+ QVERIFY(AbstractMetaClass::findClass(classes, "NamespaceA"));
+ QVERIFY(AbstractMetaClass::findClass(classes, "NamespaceA::InnerClassA"));
+ QVERIFY(AbstractMetaClass::findClass(classes, "NamespaceB"));
QCOMPARE(builder->globalEnums().size(), 2);
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode2, xmlCode2, false,
QString(), droppedEntries));
QVERIFY(builder);
- QVERIFY(!AbstractMetaClass::findClass(builder->classes(), u"ValueA"));
+ QVERIFY(!AbstractMetaClass::findClass(builder->classes(), "ValueA"));
}
{
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode2, xmlCode2, false));
QVERIFY(builder);
- QVERIFY(AbstractMetaClass::findClass(builder->classes(), u"ValueA"));
+ QVERIFY(AbstractMetaClass::findClass(builder->classes(), "ValueA"));
}
void TestDropTypeEntries::testConditionalParsing_data()
{
- const QString xml = QStringLiteral(R"(<?xml version="1.0" encoding="UTF-8"?>
+ const QString xml = R"(<?xml version="1.0" encoding="UTF-8"?>
<root>
<tag1>text</tag1>
<?if keyword1?>
<?if !keyword99?> <!-- Exclusion only -->
<tag6>text</tag6>
<?endif?>
-</root>)");
-
- const QString root = QStringLiteral("root");
- const QString tag1 = QStringLiteral("tag1");
- const QString tag2 = QStringLiteral("tag2");
- const QString tag3 = QStringLiteral("tag3");
- const QString tag4 = QStringLiteral("tag4");
- const QString tag5 = QStringLiteral("tag5");
- const QString tag6 = QStringLiteral("tag6");
- const QString keyword1 = QStringLiteral("keyword1");
- const QString keyword2 = QStringLiteral("keyword2");
+</root>)"_L1;
+
+ constexpr auto root = "root"_L1;
+ constexpr auto tag1 = "tag1"_L1;
+ constexpr auto tag2 = "tag2"_L1;
+ constexpr auto tag3 = "tag3"_L1;
+ constexpr auto tag4 = "tag4"_L1;
+ constexpr auto tag5 = "tag5"_L1;
+ constexpr auto tag6 = "tag6"_L1;
+ constexpr auto keyword1 = "keyword1"_L1;
+ constexpr auto keyword2 = "keyword2"_L1;
QTest::addColumn<QString>("xml");
QTest::addColumn<QStringList>("keywords");
void TestDropTypeEntries::testEntityParsing()
{
- const QString xml = QStringLiteral(R"(<?xml version="1.0" encoding="UTF-8"?>
+ const QString xml = R"(<?xml version="1.0" encoding="UTF-8"?>
<root>
<?entity testentity word1 word2?>
<text>bla &testentity;</text>
-</root>)");
+</root>)"_L1;
QString actual;
ConditionalStreamReader reader(xml);
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 2);
- auto klass = AbstractMetaClass::findClass(classes, u"Control");
+ auto klass = AbstractMetaClass::findClass(classes, "Control");
QVERIFY(klass);
QVERIFY(!klass->hasPrivateDestructor());
- klass = AbstractMetaClass::findClass(classes, u"Subject");
+ klass = AbstractMetaClass::findClass(classes, "Subject");
QVERIFY(klass);
QVERIFY(klass->hasPrivateDestructor());
}
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 2);
- auto klass = AbstractMetaClass::findClass(classes, u"Control");
+ auto klass = AbstractMetaClass::findClass(classes, "Control");
QVERIFY(klass);
QVERIFY(!klass->hasProtectedDestructor());
- klass = AbstractMetaClass::findClass(classes, u"Subject");
+ klass = AbstractMetaClass::findClass(classes, "Subject");
QVERIFY(klass);
QVERIFY(klass->hasProtectedDestructor());
}
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 2);
- auto klass = AbstractMetaClass::findClass(classes, u"Control");
+ auto klass = AbstractMetaClass::findClass(classes, "Control");
QVERIFY(klass);
QVERIFY(!klass->hasVirtualDestructor());
- klass = AbstractMetaClass::findClass(classes, u"Subject");
+ klass = AbstractMetaClass::findClass(classes, "Subject");
QVERIFY(klass);
QVERIFY(klass->hasVirtualDestructor());
}
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 4);
- auto klass = AbstractMetaClass::findClass(classes, u"ControlBase");
+ auto klass = AbstractMetaClass::findClass(classes, "ControlBase");
QVERIFY(klass);
QVERIFY(!klass->hasVirtualDestructor());
- klass = AbstractMetaClass::findClass(classes, u"Control");
+ klass = AbstractMetaClass::findClass(classes, "Control");
QVERIFY(klass);
QVERIFY(!klass->hasVirtualDestructor());
- klass = AbstractMetaClass::findClass(classes, u"SubjectBase");
+ klass = AbstractMetaClass::findClass(classes, "SubjectBase");
QVERIFY(klass);
QVERIFY(klass->hasVirtualDestructor());
- klass = AbstractMetaClass::findClass(classes, u"Subject");
+ klass = AbstractMetaClass::findClass(classes, "Subject");
QVERIFY(klass);
QVERIFY(klass->hasVirtualDestructor());
}
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 2);
- auto klass = AbstractMetaClass::findClass(classes, u"Control");
+ auto klass = AbstractMetaClass::findClass(classes, "Control");
QVERIFY(klass);
QVERIFY(klass->isPolymorphic());
- klass = AbstractMetaClass::findClass(classes, u"Subject");
+ klass = AbstractMetaClass::findClass(classes, "Subject");
QVERIFY(klass);
QVERIFY(klass->isPolymorphic());
}
u"A::ClassEnum");
// enum as parameter of a method
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QCOMPARE(classA->enums().size(), 1);
const auto funcs = classA->queryFunctionsByName(u"method"_s);
QVERIFY(!funcs.isEmpty());
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
QVERIFY(builder);
- AbstractMetaClassPtr classA = AbstractMetaClass::findClass(builder->classes(), u"A");
+ AbstractMetaClassPtr classA = AbstractMetaClass::findClass(builder->classes(), "A");
QVERIFY(classA);
auto enumA = classA->findEnum(u"EnumA"_s);
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
QVERIFY(builder);
- const auto classA = AbstractMetaClass::findClass(builder->classes(), u"A");
+ const auto classA = AbstractMetaClass::findClass(builder->classes(), "A");
QVERIFY(classA);
QCOMPARE(classA->enums().size(), 2);
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
QList<Include> includes = classA->typeEntry()->extraIncludes();
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- QVERIFY(AbstractMetaClass::findClass(classes, u"A"));
+ QVERIFY(AbstractMetaClass::findClass(classes, "A"));
auto *td = TypeDatabase::instance();
TypeSystemTypeEntryCPtr module = td->defaultTypeSystemType();
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 3);
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
- const auto classC = AbstractMetaClass::findClass(classes, u"C");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ const auto classC = AbstractMetaClass::findClass(classes, "C");
const auto implicitConvs = classA->implicitConversions();
QCOMPARE(implicitConvs.size(), 1);
QCOMPARE(implicitConvs.constFirst()->arguments().constFirst().type().typeEntry(),
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 2);
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
- const auto classB = AbstractMetaClass::findClass(classes, u"B");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
const auto implicitConvs = classA->implicitConversions();
QCOMPARE(implicitConvs.size(), 1);
QCOMPARE(implicitConvs.constFirst()->arguments().constFirst().type().typeEntry(),
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 3);
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
auto implicitConvs = classA->implicitConversions();
QCOMPARE(implicitConvs.size(), 2);
// Added constructors with custom types should never result in implicit converters.
- const auto classB = AbstractMetaClass::findClass(classes, u"B");
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
implicitConvs = classB->implicitConversions();
QCOMPARE(implicitConvs.size(), 0);
}
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 2);
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
- const auto classB = AbstractMetaClass::findClass(classes, u"B");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
const auto implicitConvs = classA->implicitConversions();
QCOMPARE(implicitConvs.size(), 1);
const auto &externalConvOps = classA->externalConversionOperators();
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 1);
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
QCOMPARE(classA->typeEntry()->codeSnips().size(), 1);
QString code = classA->typeEntry()->codeSnips().constFirst().code();
)";
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
QVERIFY(builder);
- const auto classA = AbstractMetaClass::findClass(builder->classes(), u"A");
+ const auto classA = AbstractMetaClass::findClass(builder->classes(), "A");
QVERIFY(classA);
DocModificationList docMods = classA->typeEntry()->docModifications();
QCOMPARE(docMods.size(), 2);
// cannot handle Qt resources.
QTemporaryDir tempDir(QDir::tempPath() + u"/shiboken_testmodifydocXXXXXX"_s);
QVERIFY2(tempDir.isValid(), qPrintable(tempDir.errorString()));
- const QString docFileName = u"a.xml"_s;
+ constexpr auto docFileName = "a.xml"_L1;
QVERIFY(QFile::copy(u":/"_s + docFileName, tempDir.filePath(docFileName)));
QtDocParser docParser;
)XML";
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
QVERIFY(builder);
- const auto classA = AbstractMetaClass::findClass(builder->classes(), u"A");
+ const auto classA = AbstractMetaClass::findClass(builder->classes(), "A");
QVERIFY(classA);
- const auto f = classA->findFunction(u"foo");
+ const auto f = classA->findFunction("foo");
QVERIFY(f);
QVERIFY(f->isUserAdded());
auto docMods = f->addedFunctionDocModifications();
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode.constData(), false));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
- const auto func = classA->findFunction(u"method");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ const auto func = classA->findFunction("method");
QVERIFY(func);
QCOMPARE(func->argumentName(1), u"otherArg");
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classB = AbstractMetaClass::findClass(classes, u"B");
- const auto func = classB->findFunction(u"method");
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
+ const auto func = classB->findFunction("method");
QVERIFY(func);
QCOMPARE(func->argumentTargetOwnership(func->ownerClass(), 0),
false, u"0.1"_s));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classB = AbstractMetaClass::findClass(classes, u"B");
- auto func = classB->findFunction(u"call");
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
+ auto func = classB->findFunction("call");
QCOMPARE(func->modifications().size(), 1);
QCOMPARE(func->modifications().at(0).argument_mods().size(), 1);
QVERIFY(func->modifications().at(0).argument_mods().at(0).resetAfterUse());
- const auto classC = AbstractMetaClass::findClass(classes, u"C");
+ const auto classC = AbstractMetaClass::findClass(classes, "C");
QVERIFY(classC);
- func = classC->findFunction(u"call");
+ func = classC->findFunction("call");
QCOMPARE(func->modifications().size(), 1);
QCOMPARE(func->modifications().at(0).argument_mods().size(), 1);
QVERIFY(func->modifications().at(0).argument_mods().at(0).resetAfterUse());
- func = classC->findFunction(u"call2");
+ func = classC->findFunction("call2");
QCOMPARE(func->modifications().size(), 1);
QCOMPARE(func->modifications().at(0).argument_mods().size(), 1);
QVERIFY(func->modifications().at(0).argument_mods().at(0).resetAfterUse());
- AbstractMetaClassCPtr classD = AbstractMetaClass::findClass(classes, u"D");
+ AbstractMetaClassCPtr classD = AbstractMetaClass::findClass(classes, "D");
QVERIFY(classD);
- func = classD->findFunction(u"call");
+ func = classD->findFunction("call");
QCOMPARE(func->modifications().size(), 1);
QCOMPARE(func->modifications().at(0).argument_mods().size(), 1);
QVERIFY(func->modifications().at(0).argument_mods().at(0).resetAfterUse());
- func = classD->findFunction(u"call2");
+ func = classD->findFunction("call2");
QCOMPARE(func->modifications().size(), 1);
QCOMPARE(func->modifications().at(0).argument_mods().size(), 1);
QVERIFY(func->modifications().at(0).argument_mods().at(0).resetAfterUse());
- const auto classE = AbstractMetaClass::findClass(classes, u"E");
+ const auto classE = AbstractMetaClass::findClass(classes, "E");
QVERIFY(classE);
- func = classE->findFunction(u"call");
+ func = classE->findFunction("call");
QVERIFY(func);
QCOMPARE(func->modifications().size(), 1);
QCOMPARE(func->modifications().at(0).argument_mods().size(), 1);
QVERIFY(func->modifications().at(0).argument_mods().at(0).resetAfterUse());
- func = classE->findFunction(u"call2");
+ func = classE->findFunction("call2");
QVERIFY(func);
QCOMPARE(func->modifications().size(), 1);
QCOMPARE(func->modifications().at(0).argument_mods().size(), 1);
false, u"0.1"_s));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classB = AbstractMetaClass::findClass(classes, u"B");
- auto func = classB->findFunction(u"method");
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
+ auto func = classB->findFunction("method");
auto returnOwnership = func->argumentTargetOwnership(func->ownerClass(), 0);
QCOMPARE(returnOwnership, TypeSystem::CppOwnership);
- func = classB->findFunction(u"methodB");
+ func = classB->findFunction("methodB");
returnOwnership = func->argumentTargetOwnership(func->ownerClass(), 0);
QVERIFY(returnOwnership != TypeSystem::CppOwnership);
}
false, u"0.1"_s));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
// Nothing specified, true
- const auto f1 = classA->findFunction(u"f1");
+ const auto f1 = classA->findFunction("f1");
QVERIFY(f1);
QVERIFY(!f1->allowThread());
// 'auto' specified, should be false for nontrivial function
- const auto f2 = classA->findFunction(u"f2");
+ const auto f2 = classA->findFunction("f2");
QVERIFY(f2);
QVERIFY(f2->allowThread());
// 'no' specified, should be false
- const auto f3 = classA->findFunction(u"f3");
+ const auto f3 = classA->findFunction("f3");
QVERIFY(f3);
QVERIFY(!f3->allowThread());
// Nothing specified, should be false for simple getter
- const auto getter1 = classA->findFunction(u"getter1");
+ const auto getter1 = classA->findFunction("getter1");
QVERIFY(getter1);
QVERIFY(!getter1->allowThread());
// Forced to true simple getter
- const auto getter2 = classA->findFunction(u"getter2");
+ const auto getter2 = classA->findFunction("getter2");
QVERIFY(getter2);
QVERIFY(getter2->allowThread()); // Forced to true simple getter
}
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode.constData(), xmlCode.constData(), false));
QVERIFY(builder);
- const auto classA = AbstractMetaClass::findClass(builder->classes(), u"A");
+ const auto classA = AbstractMetaClass::findClass(builder->classes(), "A");
QVERIFY(classA);
- auto f = classA->findFunction(QStringLiteral("unspecified"));
+ auto f = classA->findFunction("unspecified");
QVERIFY(f);
QCOMPARE(f->exceptionSpecification(), ExceptionSpecification::Unknown);
QCOMPARE(f->generateExceptionHandling(), expectedGenerateUnspecified);
QCOMPARE(f->allowThread(), expectedAllowThread);
- f = classA->findFunction(QStringLiteral("nonThrowing"));
+ f = classA->findFunction("nonThrowing");
QVERIFY(f);
QCOMPARE(f->exceptionSpecification(), ExceptionSpecification::NoExcept);
QCOMPARE(f->generateExceptionHandling(), expectedGenerateNonThrowing);
- f = classA->findFunction(QStringLiteral("throwing"));
+ f = classA->findFunction("throwing");
QVERIFY(f);
QCOMPARE(f->exceptionSpecification(), ExceptionSpecification::Throws);
QCOMPARE(f->generateExceptionHandling(), expectedGenerateThrowing);
void TestModifyFunction::testSnakeCaseRenaming_data()
{
- QTest::addColumn<QString>("name");
- QTest::addColumn<QString>("expected");
+ QTest::addColumn<QLatin1StringView>("name");
+ QTest::addColumn<QLatin1StringView>("expected");
QTest::newRow("s1")
- << QStringLiteral("snakeCaseFunc") << QStringLiteral("snake_case_func");
+ << "snakeCaseFunc"_L1 << "snake_case_func"_L1;
QTest::newRow("s2")
- << QStringLiteral("SnakeCaseFunc") << QStringLiteral("snake_case_func");
+ << "SnakeCaseFunc"_L1 << "snake_case_func"_L1;
QTest::newRow("consecutive-uppercase")
- << QStringLiteral("snakeCAseFunc") << QStringLiteral("snakeCAseFunc");
+ << "snakeCAseFunc"_L1 << "snakeCAseFunc"_L1;
}
void TestModifyFunction::testSnakeCaseRenaming()
{
- QFETCH(QString, name);
- QFETCH(QString, expected);
+ QFETCH(QLatin1StringView, name);
+ QFETCH(QLatin1StringView, expected);
const QString actual = AbstractMetaBuilder::getSnakeCaseName(name);
QCOMPARE(actual, expected);
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 4);
- const auto classD = AbstractMetaClass::findClass(classes, u"D");
+ const auto classD = AbstractMetaClass::findClass(classes, "D");
bool functionFound = false;
for (const auto &f : classD->functions()) {
if (f->name() == u"theBug") {
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto ns = AbstractMetaClass::findClass(classes, u"Namespace");
+ const auto ns = AbstractMetaClass::findClass(classes, "Namespace");
QVERIFY(ns);
auto metaEnum = ns->findEnum(u"Option"_s);
QVERIFY(metaEnum.has_value());
- const auto func = ns->findFunction(u"foo");
+ const auto func = ns->findFunction("foo");
QVERIFY(func);
}
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto ons = AbstractMetaClass::findClass(classes, u"OuterNamespace");
+ const auto ons = AbstractMetaClass::findClass(classes, "OuterNamespace");
QVERIFY(ons);
- const auto ins = AbstractMetaClass::findClass(classes, u"OuterNamespace::InnerNamespace");
+ const auto ins = AbstractMetaClass::findClass(classes, "OuterNamespace::InnerNamespace");
QVERIFY(ins);
- const auto sc = AbstractMetaClass::findClass(classes, u"OuterNamespace::InnerNamespace::SomeClass");
+ const auto sc = AbstractMetaClass::findClass(classes, "OuterNamespace::InnerNamespace::SomeClass");
QVERIFY(sc);
- const auto meth = sc->findFunction(u"method");
+ const auto meth = sc->findFunction("method");
QVERIFY(meth);
}
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto ons = AbstractMetaClass::findClass(classes, u"OuterNamespace");
+ const auto ons = AbstractMetaClass::findClass(classes, "OuterNamespace");
QVERIFY(ons);
- const auto ins = AbstractMetaClass::findClass(classes, u"OuterNamespace::InnerNamespace");
+ const auto ins = AbstractMetaClass::findClass(classes, "OuterNamespace::InnerNamespace");
QVERIFY(ins);
QCOMPARE(ins->functions().size(), 1);
QCOMPARE(ins->typeEntry()->codeSnips().size(), 1);
QCOMPARE(snip.code().trimmed(), u"custom_code2();");
const auto sc =
- AbstractMetaClass::findClass(classes, u"OuterNamespace::InnerNamespace::SomeClass");
+ AbstractMetaClass::findClass(classes, "OuterNamespace::InnerNamespace::SomeClass");
QVERIFY(sc);
QCOMPARE(sc->functions().size(), 2); // default constructor and removed method
const auto removedFunc = sc->functions().constLast();
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 2);
- const auto nspace = AbstractMetaClass::findClass(classes, u"Namespace");
+ const auto nspace = AbstractMetaClass::findClass(classes, "Namespace");
QVERIFY(nspace);
- const auto cls1 = AbstractMetaClass::findClass(classes, u"SomeClass");
+ const auto cls1 = AbstractMetaClass::findClass(classes, "SomeClass");
QVERIFY(cls1);
- const auto cls2 = AbstractMetaClass::findClass(classes, u"Namespace::SomeClass");
+ const auto cls2 = AbstractMetaClass::findClass(classes, "Namespace::SomeClass");
QVERIFY(cls2);
QCOMPARE(cls1, cls2);
QCOMPARE(cls1->name(), u"SomeClass");
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 1);
- const auto classB = AbstractMetaClass::findClass(classes, u"B");
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
QVERIFY(classB);
auto typeEntry = TypeDatabase::instance()->findPrimitiveType(u"A"_s);
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classB = AbstractMetaClass::findClass(classes, u"B");
- const auto func = classB->findFunction(u"keepObject");
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
+ const auto func = classB->findFunction("keepObject");
QVERIFY(func);
const auto refCount =
func->modifications().constFirst().argument_mods().constFirst().referenceCounts().constFirst();
false, u"0.1"_s));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classB = AbstractMetaClass::findClass(classes, u"B");
- const auto func = classB->findFunction(u"keepObject");
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
+ const auto func = classB->findFunction("keepObject");
QVERIFY(func);
const auto refCount =
func->modifications().constFirst().argument_mods().constFirst().referenceCounts().constFirst();
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classB = AbstractMetaClass::findClass(classes, u"B");
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
QVERIFY(classB);
- const auto func = classB->findFunction(u"dummy");
+ const auto func = classB->findFunction("dummy");
QVERIFY(func);
QCOMPARE(func->arguments().constFirst().type().minimalSignature(), u"A*&");
}
#include "testremovefield.h"
#include <QtTest/QTest>
#include "testutil.h"
+#include <abstractmetaargument.h>
#include <abstractmetafield.h>
+#include <abstractmetafunction.h>
+#include <abstractmetatype.h>
#include <abstractmetalang.h>
#include <typesystem.h>
+using namespace Qt::StringLiterals;
+
void TestRemoveField::testRemoveField()
{
const char cppCode[] = "\
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
QCOMPARE(classA->fields().size(), 1);
const AbstractMetaField &fieldA = classA->fields().constFirst();
QCOMPARE(fieldA.name(), u"fieldA");
}
+// Verify that 'static constexpr' fields are seen as static/const and
+// appear fully qualified for function parameter default values.
+void TestRemoveField::testConstExprField()
+{
+ const char cppCode[] = R"(
+struct A {
+ static constexpr int constExprField = 44;
+
+ void f(int iParam=constExprField);
+};
+)";
+
+ const char xmlCode[] = R"(
+<typesystem package="Foo">
+ <value-type name='A'/>
+</typesystem>
+)";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ const auto &fields = classA->fields();
+ QCOMPARE(fields.size(), 1);
+ QVERIFY(fields.constFirst().isStatic());
+ QVERIFY(fields.constFirst().type().isConstant());
+ const auto function = classA->findFunction("f"_L1);
+ QVERIFY(function);
+ const auto &arguments = function->arguments();
+ QCOMPARE(arguments.size(), 1);
+ QCOMPARE(arguments.constFirst().defaultValueExpression(), "A::constExprField"_L1);
+}
+
QTEST_APPLESS_MAIN(TestRemoveField)
Q_OBJECT
private slots:
void testRemoveField();
+ void testConstExprField();
};
#endif
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 3);
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
- const auto classB = AbstractMetaClass::findClass(classes, u"B");
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
QVERIFY(classB);
- const auto classC = AbstractMetaClass::findClass(classes, u"C");
+ const auto classC = AbstractMetaClass::findClass(classes, "C");
QVERIFY(classC);
const auto implConv = classC->implicitConversions();
QCOMPARE(implConv.size(), 1);
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
QCOMPARE(classA->functions().size(), 14);
QStringList removedSignatures;
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classD = AbstractMetaClass::findClass(classes, u"A::D");
+ const auto classD = AbstractMetaClass::findClass(classes, "A::D");
QVERIFY(classD);
- const auto meth = classD->findFunction(u"method");
+ const auto meth = classD->findFunction("method");
QVERIFY(meth);
QVERIFY(meth);
}
fixture->classType = AbstractMetaType(fixture->klass->typeEntry());
fixture->classType.decideUsagePattern();
- for (const auto &f : fixture->klass->findFunctions(u"Test"_s)) {
+ for (const auto &f : fixture->klass->findFunctions("Test")) {
if (f->functionType() == AbstractMetaFunction::ConstructorFunction
&& f->arguments().size() == 1) {
const auto type = f->arguments().constFirst().type();
if (fixture->intType.isVoid() || fixture->stringType.isVoid())
return -3;
- auto listFunc = fixture->klass->findFunction(u"listFunc"_s);
+ auto listFunc = fixture->klass->findFunction("listFunc");
if (!listFunc || listFunc->arguments().size() != 1)
return -3;
fixture->listType = listFunc->arguments().constFirst().type();
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto testClass = AbstractMetaClass::findClass(classes, u"Test");
+ const auto testClass = AbstractMetaClass::findClass(classes, "Test");
QVERIFY(testClass);
auto *tdb = TypeDatabase::instance();
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
QCOMPARE(classA->functions().size(), 4);
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
QCOMPARE(classA->functions().size(), 4);
- const auto classB = AbstractMetaClass::findClass(classes, u"B");
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
QVERIFY(classB);
QCOMPARE(classB->functions().size(), 4);
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 1);
- const auto testClass = AbstractMetaClass::findClass(classes, u"Test");
+ const auto testClass = AbstractMetaClass::findClass(classes, "Test");
QVERIFY(testClass);
const auto &functions = testClass->functions();
// 6 operators should be synthesized
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classB = AbstractMetaClass::findClass(classes, u"Bookmarks");
+ const auto classB = AbstractMetaClass::findClass(classes, "Bookmarks");
QVERIFY(classB);
- const auto func = classB->findFunction(u"list");
+ const auto func = classB->findFunction("list");
QVERIFY(func);
AbstractMetaType funcType = func->type();
QVERIFY(!funcType.isVoid());
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classB = AbstractMetaClass::findClass(classes, u"B");
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
QVERIFY(classB);
QVERIFY(!classB->baseClass());
QVERIFY(classB->baseClassName().isEmpty());
- const auto func = classB->findFunction(u"foo");
+ const auto func = classB->findFunction("foo");
QVERIFY(func);
AbstractMetaType argType = func->arguments().constFirst().type();
QCOMPARE(argType.instantiations().size(), 1);
QCOMPARE(templates.size(), 1);
AbstractMetaClassCPtr list = templates.constFirst();
// Verify that the parameter of "void append(List l)" gets fixed to "List<T>"
- const auto append = list->findFunction(QStringLiteral("append"));
+ const auto append = list->findFunction("append");
QVERIFY(append);
QCOMPARE(append->arguments().size(), 1);
QCOMPARE(append->arguments().at(0).type().cppSignature(), u"List<T>");
// Verify that the parameter of "void erase(Iterator)" is not modified
- const auto erase = list->findFunction(QStringLiteral("erase"));
+ const auto erase = list->findFunction("erase");
QVERIFY(erase);
QCOMPARE(erase->arguments().size(), 1);
QCOMPARE(erase->arguments().at(0).type().cppSignature(), u"List::Iterator");
QCOMPARE(classes.size(), 2);
QCOMPARE(templates.size(), 1);
- const auto foobars = AbstractMetaClass::findClass(classes, u"FooBars");
+ const auto foobars = AbstractMetaClass::findClass(classes, "FooBars");
QCOMPARE(foobars->functions().size(), 4);
AbstractMetaClassCPtr lc = templates.constFirst();
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classB = AbstractMetaClass::findClass(classes, u"B");
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
QVERIFY(classB);
QVERIFY(!classB->baseClass());
QVERIFY(classB->baseClassName().isEmpty());
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classB = AbstractMetaClass::findClass(classes, u"Namespace::B");
+ const auto classB = AbstractMetaClass::findClass(classes, "Namespace::B");
QVERIFY(classB);
QVERIFY(!classB->baseClass());
QVERIFY(classB->baseClassName().isEmpty());
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 3);
- const auto base = AbstractMetaClass::findClass(classes, u"BaseTemplateClass");
+ const auto base = AbstractMetaClass::findClass(classes, "BaseTemplateClass");
QVERIFY(base);
- const auto one = AbstractMetaClass::findClass(classes, u"TypeOneClass");
+ const auto one = AbstractMetaClass::findClass(classes, "TypeOneClass");
QVERIFY(one);
QCOMPARE(one->templateBaseClass(), base);
QCOMPARE(one->functions().size(), base->functions().size());
AbstractMetaClassList classes = builder->classes();
QCOMPARE(classes.size(), 1);
- const auto vector = AbstractMetaClass::findClass(classes, u"IntVector");
+ const auto vector = AbstractMetaClass::findClass(classes, "IntVector");
QVERIFY(vector);
auto baseContainer = vector->typeEntry()->baseContainerType();
QVERIFY(baseContainer);
ContainerTypeEntry::ListContainer);
QCOMPARE(vector->functions().size(), 4);
- const auto method = vector->findFunction(u"method");
+ const auto method = vector->findFunction("method");
QVERIFY(method);
QCOMPARE(method->signature(), u"method(const Vector<int> & vector)");
- const auto otherMethod = vector->findFunction(u"otherMethod");
+ const auto otherMethod = vector->findFunction("otherMethod");
QVERIFY(otherMethod);
QCOMPARE(otherMethod->signature(), u"otherMethod()");
QVERIFY(!otherMethod->type().isVoid());
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto optional = AbstractMetaClass::findClass(classes, u"Optional");
+ const auto optional = AbstractMetaClass::findClass(classes, "Optional");
QVERIFY(optional);
// Find the typedef'ed class
- const auto optionalInt = AbstractMetaClass::findClass(classes, u"IntOptional");
+ const auto optionalInt = AbstractMetaClass::findClass(classes, "IntOptional");
QVERIFY(optionalInt);
QCOMPARE(optionalInt->templateBaseClass(), optional);
// Find the class typedef'ed in the typesystem XML
- const auto xmlOptionalInt = AbstractMetaClass::findClass(classes, u"XmlIntOptional");
+ const auto xmlOptionalInt = AbstractMetaClass::findClass(classes, "XmlIntOptional");
QVERIFY(xmlOptionalInt);
QCOMPARE(xmlOptionalInt->templateBaseClass(), optional);
// Check whether the value() method now has an 'int' return
- const auto valueMethod = optionalInt->findFunction(u"value");
+ const auto valueMethod = optionalInt->findFunction("value");
QVERIFY(valueMethod);
QCOMPARE(valueMethod->type().cppSignature(), u"int");
// ditto for typesystem XML
- const auto xmlValueMethod = xmlOptionalInt->findFunction(u"value");
+ const auto xmlValueMethod = xmlOptionalInt->findFunction("value");
QVERIFY(xmlValueMethod);
QCOMPARE(xmlValueMethod->type().cppSignature(), u"int");
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto testClass = AbstractMetaClass::findClass(classes, u"Test");
+ const auto testClass = AbstractMetaClass::findClass(classes, "Test");
QVERIFY(testClass);
auto fields = testClass->fields();
QCOMPARE(fieldType.name(), u"Container1");
QCOMPARE(fieldType.instantiations().size(), 1);
- const auto derived = AbstractMetaClass::findClass(classes, u"Derived");
+ const auto derived = AbstractMetaClass::findClass(classes, "Derived");
QVERIFY(derived);
auto base = derived->templateBaseClass();
QVERIFY(base);
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto rev0 = AbstractMetaClass::findClass(classes, u"Rev_0");
+ const auto rev0 = AbstractMetaClass::findClass(classes, "Rev_0");
QCOMPARE(rev0->typeEntry()->revision(), 0);
- const auto rev1 = AbstractMetaClass::findClass(classes, u"Rev_1");
+ const auto rev1 = AbstractMetaClass::findClass(classes, "Rev_1");
QCOMPARE(rev1->typeEntry()->revision(), 1);
- const auto rev2 = AbstractMetaClass::findClass(classes, u"Rev_2");
+ const auto rev2 = AbstractMetaClass::findClass(classes, "Rev_2");
QCOMPARE(rev2->typeEntry()->revision(), 2);
auto rev3 = rev2->findEnum(u"Rev_3"_s);
auto *td = TypeDatabase::instance(true);
if (apiVersion.isEmpty())
TypeDatabase::clearApiVersions();
- else if (!TypeDatabase::setApiVersion(QStringLiteral("*"), apiVersion))
+ else if (!TypeDatabase::setApiVersion(QLatin1StringView("*"), apiVersion))
return nullptr;
td->setDropTypeEntries(dropTypeEntries);
QBuffer buffer;
return nullptr;
buffer.close();
// parse C++ code
- QTemporaryFile tempSource(QDir::tempPath() + QStringLiteral("/st_XXXXXX_main.cpp"));
+ QTemporaryFile tempSource(QDir::tempPath() + QLatin1StringView("/st_XXXXXX_main.cpp"));
if (!tempSource.open()) {
qWarning().noquote().nospace() << "Creation of temporary file failed: "
<< tempSource.errorString();
AbstractMetaClassList classes = builder->classes();
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
QVERIFY(classA->typeEntry()->hasDefaultConstructor());
QCOMPARE(classA->typeEntry()->defaultConstructor(), u"A(0, 0)");
- const auto classB = AbstractMetaClass::findClass(classes, u"B");
+ const auto classB = AbstractMetaClass::findClass(classes, "B");
QVERIFY(classB);
QVERIFY(!classB->typeEntry()->hasDefaultConstructor());
}
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
- const auto addedFunc = classA->findFunction(u"a");
+ const auto addedFunc = classA->findFunction("a");
QVERIFY(addedFunc);
QCOMPARE(addedFunc->arguments().size(), 0);
}
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
- const auto addedFunc = classA->findFunction(u"a");
+ const auto addedFunc = classA->findFunction("a");
QVERIFY(addedFunc);
QCOMPARE(addedFunc->arguments().size(), 0);
QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
QVERIFY(builder);
AbstractMetaClassList classes = builder->classes();
- const auto classA = AbstractMetaClass::findClass(classes, u"A");
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
QVERIFY(classA);
- const auto addedFunc = classA->findFunction(u"a");
+ const auto addedFunc = classA->findFunction("a");
QVERIFY(addedFunc);
QCOMPARE(addedFunc->arguments().size(), 1);
void putRawChar(char c) { m_str << c; }
TextStream &operator<<(QStringView v) { putString(v); return *this; }
+ TextStream &operator<<(const QString &qs) { putString(QStringView{qs}); return *this; }
+ TextStream &operator<<(QLatin1StringView lv) { putString(lv.constData()); return *this; }
+ TextStream &operator<<(QUtf8StringView uv) { putString(uv.data()); return *this; }
+ TextStream &operator<<(const QByteArray &ba) { putString(ba.constData()); return *this; }
TextStream &operator<<(QChar c) { putChar(c); return *this; }
TextStream &operator<<(const char *s) { putString(s); return *this; }
TextStream &operator<<(char c) { putChar(c); return *this; }
void rstItalic(TextStream &s);
void rstItalicOff(TextStream &s);
+inline TextStream &operator<<(TextStream &str, QAnyStringView asv)
+{
+ asv.visit([&str](auto s) { str << s; });
+ return str;
+}
+
/// Format an aligned field
template <class T>
class AlignedField
static QString wildcardToRegExp(QString w)
{
w.replace(u'?', u'.');
- w.replace(u'*', QStringLiteral(".*"));
+ w.replace(u'*', ".*"_L1);
return w;
}
break;
case OptionSource::CommandLineSingleDash:
if (key.startsWith(u'T')) { // "-T/path" ends up a bool option
- m_options->m_typesystemPaths += key.sliced(1).split(QDir::listSeparator());
+ m_options->m_typesystemPaths += key.sliced(1).split(QDir::listSeparator(),
+ Qt::SkipEmptyParts);
return true;
}
break;
}
if (key == u"typesystem-paths") {
- m_options->m_typesystemPaths += value.split(QDir::listSeparator());
+ m_options->m_typesystemPaths += value.split(QDir::listSeparator(),
+ Qt::SkipEmptyParts);
return true;
}
if (key == u"force-process-system-include-paths") {
- m_options->m_forceProcessSystemIncludes += value.split(QDir::listSeparator());
+ m_options->m_forceProcessSystemIncludes += value.split(QDir::listSeparator(),
+ Qt::SkipEmptyParts);
return true;
}
// Environment TYPESYSTEMPATH
if (qEnvironmentVariableIsSet(ENV_TYPESYSTEMPATH)) {
d->m_typesystemPaths
- += qEnvironmentVariable(ENV_TYPESYSTEMPATH).split(QDir::listSeparator());
+ += qEnvironmentVariable(ENV_TYPESYSTEMPATH).split(QDir::listSeparator(),
+ Qt::SkipEmptyParts);
}
d->addBuiltInType(TypeEntryPtr(new VoidTypeEntry()));
static bool firstTime = true;
if (firstTime) {
firstTime = false;
- for (auto t : {"char", "short", "int", "long"}) {
- const QString intType = QLatin1StringView(t);
+ for (const auto &intType : {"char"_L1, "short"_L1, "int"_L1, "long"_L1}) {
if (!TypeDatabase::instance()->findType(u'u' + intType)) {
IntTypeNormalizationEntry entry;
- entry.replacement = QStringLiteral("unsigned ") + intType;
- entry.regex.setPattern(QStringLiteral("\\bu") + intType + QStringLiteral("\\b"));
+ entry.replacement = "unsigned "_L1 + intType;
+ entry.regex.setPattern("\\bu"_L1 + intType + "\\b"_L1);
Q_ASSERT(entry.regex.isValid());
result.append(entry);
}
{
QStringList result = d->m_typesystemKeywords;
for (const auto &d : d->m_dropTypeEntries)
- result.append(QStringLiteral("no_") + d);
+ result.append("no_"_L1 + d);
switch (clang::emulatedCompilerLanguageLevel()) {
case LanguageLevel::Cpp11:
qsizetype lastPos = 0;
for (qsizetype a = 0, aSize = asteriskPositions.size(); a < aSize; ++a) {
if (a)
- pattern.append(QStringLiteral(".*"));
+ pattern.append(".*"_L1);
const auto nextPos = asteriskPositions.at(a);
if (nextPos > lastPos)
pattern.append(QRegularExpression::escape(warning.mid(lastPos, nextPos - lastPos)));
return false;
auto wit = std::find_if(d->m_suppressedWarnings.cbegin(), d->m_suppressedWarnings.cend(),
[&s] (const SuppressedWarning &e) {
- return e.pattern.match(s).hasMatch();
+ return e.pattern.matchView(s).hasMatch();
});
const bool found = wit != d->m_suppressedWarnings.cend();
if (found)
Q_ASSERT(pyUnicodeEntry && pyUnicodeEntry->isCustom());
auto pyUnicodeCustomEntry = std::static_pointer_cast<CustomTypeEntry>(pyUnicodeEntry);
- const QString stdString = u"std::string"_s;
+ constexpr auto stdString = "std::string"_L1;
if (!m_entries.contains(stdString)) {
addBuiltInCppStringPrimitiveType(stdString, u"std::string_view"_s,
root, rootPackage,
pyUnicodeCustomEntry);
}
- const QString stdWString = u"std::wstring"_s;
+ constexpr auto stdWString = "std::wstring"_L1;
if (!m_entries.contains(stdWString)) {
addBuiltInCppStringPrimitiveType(stdWString, u"std::wstring_view"_s,
root, rootPackage,
QString Scanner::msgParseError(const QString &why) const
{
- return QStringLiteral("TypeParser: Unable to parse \"")
- + QString(m_chars, m_length) + QStringLiteral("\": ") + why;
+ return "TypeParser: Unable to parse \""_L1
+ + QString(m_chars, m_length) + "\": "_L1 + why;
}
TypeInfo TypeParser::parse(const QString &str, QString *errorMessage)
stack.top().setReferenceType(RValueReference);
break;
case RValueReference:
- const QString message = scanner.msgParseError(QStringLiteral("Too many '&' qualifiers"));
+ const QString message = scanner.msgParseError("Too many '&' qualifiers"_L1);
if (errorMessage)
*errorMessage = message;
else
case Scanner::OpenParenToken: // function pointers not supported
case Scanner::CloseParenToken: {
- const QString message = scanner.msgParseError(QStringLiteral("Function pointers are not supported"));
+ const QString message = scanner.msgParseError("Function pointers are not supported"_L1);
if (errorMessage)
*errorMessage = message;
else
CodeSnipList m_codeSnips;
TypeSystem::SnakeCase m_snakeCase = TypeSystem::SnakeCase::Disabled;
+ QString m_subModuleOf;
+ QString m_namespaceBegin;
+ QString m_namespaceEnd;
};
TypeSystemTypeEntry::TypeSystemTypeEntry(const QString &entryName, const QVersionNumber &vr,
d->m_codeSnips.append(codeSnip);
}
+QString TypeSystemTypeEntry::subModuleOf() const
+{
+ S_D(const TypeSystemTypeEntry);
+ return d->m_subModuleOf;
+}
+
+void TypeSystemTypeEntry::setSubModule(const QString &s)
+{
+ S_D(TypeSystemTypeEntry);
+ d->m_subModuleOf = s;
+}
+
+const QString &TypeSystemTypeEntry::namespaceBegin() const
+{
+ S_D(const TypeSystemTypeEntry);
+ return d->m_namespaceBegin;
+}
+
+void TypeSystemTypeEntry::setNamespaceBegin(const QString &p)
+{
+ S_D(TypeSystemTypeEntry);
+ d->m_namespaceBegin = p;
+}
+
+const QString &TypeSystemTypeEntry::namespaceEnd() const
+{
+ S_D(const TypeSystemTypeEntry);
+ return d->m_namespaceEnd;
+}
+
+void TypeSystemTypeEntry::setNamespaceEnd(const QString &n)
+{
+ S_D(TypeSystemTypeEntry);
+ d->m_namespaceEnd = n;
+}
+
TypeSystem::SnakeCase TypeSystemTypeEntry::snakeCase() const
{
S_D(const TypeSystemTypeEntry);
QStringList m_rejectedEnums;
FlagsTypeEntryPtr m_flags;
QString m_cppType;
+ QString m_docFile;
TypeSystem::PythonEnumType m_pythonEnumType = TypeSystem::PythonEnumType::Unspecified;
};
return d->m_rejectedEnums;
}
+QString EnumTypeEntry::docFile() const
+{
+ S_D(const EnumTypeEntry);
+ return d->m_docFile;
+}
+
+void EnumTypeEntry::setDocFile(const QString &df)
+{
+ S_D(EnumTypeEntry);
+ d->m_docFile = df;
+}
+
TypeEntry *EnumTypeEntry::clone() const
{
S_D(const EnumTypeEntry);
TypeEntry::useAsTypedef(source);
d->m_qualifiedCppName = source->qualifiedCppName();
d->m_targetType = source->targetType();
+ d->m_typeFlags.setFlag(ComplexTypeEntry::Typedef);
}
ComplexTypeEntry::ComplexTypeEntry(ComplexTypeEntryPrivate *d) :
}
QStringList m_signatures;
+ QString m_docFile;
};
FunctionTypeEntry::FunctionTypeEntry(const QString &entryName, const QString &signature,
return d->m_signatures.contains(signature);
}
+QString FunctionTypeEntry::docFile() const
+{
+ S_D(const FunctionTypeEntry);
+ return d->m_docFile;
+}
+
+void FunctionTypeEntry::setDocFile(const QString &df)
+{
+ S_D(FunctionTypeEntry);
+ d->m_docFile = df;
+}
+
TypeEntry *FunctionTypeEntry::clone() const
{
S_D(const FunctionTypeEntry);
CodeSnipPositionBeginning,
CodeSnipPositionEnd,
CodeSnipPositionDeclaration,
+ CodeSnipPositionPyOverride,
CodeSnipPositionAny
};
using TypeSystemTypeEntryCPtr = std::shared_ptr<const TypeSystemTypeEntry>;
using ValueTypeEntryCPtr = std::shared_ptr<const ValueTypeEntry>;
+using ComplexTypeEntryCList = QList<ComplexTypeEntryCPtr>;
using ContainerTypeEntryCList = QList<ContainerTypeEntryCPtr>;
using NamespaceTypeEntryList = QList<NamespaceTypeEntryPtr>;
using PrimitiveTypeEntryCList = QList<PrimitiveTypeEntryCPtr>;
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "typesystemparser_p.h"
+#include "anystringview_helpers.h"
#include "addedfunction.h"
#include "codesnip.h"
#include "enumtypeentry.h"
using namespace Qt::StringLiterals;
-static inline QString allowThreadAttribute() { return QStringLiteral("allow-thread"); }
-static inline QString checkFunctionAttribute() { return QStringLiteral("check-function"); }
-static inline QString copyableAttribute() { return QStringLiteral("copyable"); }
-static inline QString accessAttribute() { return QStringLiteral("access"); }
-static inline QString actionAttribute() { return QStringLiteral("action"); }
-static inline QString quoteAfterLineAttribute() { return QStringLiteral("quote-after-line"); }
-static inline QString quoteBeforeLineAttribute() { return QStringLiteral("quote-before-line"); }
-static inline QString textAttribute() { return QStringLiteral("text"); }
-static inline QString nameAttribute() { return QStringLiteral("name"); }
-static inline QString sinceAttribute() { return QStringLiteral("since"); }
-static inline QString untilAttribute() { return QStringLiteral("until"); }
-static inline QString defaultSuperclassAttribute() { return QStringLiteral("default-superclass"); }
-static inline QString deleteInMainThreadAttribute() { return QStringLiteral("delete-in-main-thread"); }
-static inline QString deprecatedAttribute() { return QStringLiteral("deprecated"); }
-static inline QString disableWrapperAttribute() { return QStringLiteral("disable-wrapper"); }
-static inline QString exceptionHandlingAttribute() { return QStringLiteral("exception-handling"); }
-static inline QString extensibleAttribute() { return QStringLiteral("extensible"); }
-static inline QString fileNameAttribute() { return QStringLiteral("file-name"); }
-static inline QString flagsAttribute() { return QStringLiteral("flags"); }
-static inline QString forceAbstractAttribute() { return QStringLiteral("force-abstract"); }
-static inline QString forceIntegerAttribute() { return QStringLiteral("force-integer"); }
-static inline QString formatAttribute() { return QStringLiteral("format"); }
-static inline QString generateUsingAttribute() { return QStringLiteral("generate-using"); }
-static inline QString generateFunctionsAttribute() { return QStringLiteral("generate-functions"); }
-static inline QString classAttribute() { return QStringLiteral("class"); }
-static inline QString generateAttribute() { return QStringLiteral("generate"); }
-static inline QString generateGetSetDefAttribute() { return QStringLiteral("generate-getsetdef"); }
-static inline QString genericClassAttribute() { return QStringLiteral("generic-class"); }
-static inline QString indexAttribute() { return QStringLiteral("index"); }
-static inline QString invalidateAfterUseAttribute() { return QStringLiteral("invalidate-after-use"); }
-static inline QString isNullAttribute() { return QStringLiteral("isNull"); }
-static inline QString locationAttribute() { return QStringLiteral("location"); }
-static inline QString modifiedTypeAttribute() { return QStringLiteral("modified-type"); }
-static inline QString opaqueContainerAttribute() { return QStringLiteral("opaque-containers"); }
-static inline QString operatorBoolAttribute() { return QStringLiteral("operator-bool"); }
-static inline QString parentManagementAttribute() { return QStringLiteral("parent-management"); }
-static inline QString pyiTypeAttribute() { return QStringLiteral("pyi-type"); }
-static inline QString overloadNumberAttribute() { return QStringLiteral("overload-number"); }
-static inline QString ownershipAttribute() { return QStringLiteral("owner"); }
-static inline QString packageAttribute() { return QStringLiteral("package"); }
-static inline QString positionAttribute() { return QStringLiteral("position"); }
-static inline QString preferredConversionAttribute() { return QStringLiteral("preferred-conversion"); }
-static inline QString preferredTargetLangTypeAttribute() { return QStringLiteral("preferred-target-lang-type"); }
-static inline QString pythonEnumTypeAttribute() { return QStringLiteral("python-type"); }
-static inline QString cppEnumTypeAttribute() { return QStringLiteral("cpp-type"); }
-static inline QString qtMetaTypeAttribute() { return QStringLiteral("qt-register-metatype"); }
-static inline QString removeAttribute() { return QStringLiteral("remove"); }
-static inline QString renameAttribute() { return QStringLiteral("rename"); }
-static inline QString readAttribute() { return QStringLiteral("read"); }
-static inline QString targetLangNameAttribute() { return QStringLiteral("target-lang-name"); }
-static inline QString writeAttribute() { return QStringLiteral("write"); }
-static inline QString opaqueContainerFieldAttribute() { return QStringLiteral("opaque-container"); }
-static inline QString replaceAttribute() { return QStringLiteral("replace"); }
-static inline QString toAttribute() { return QStringLiteral("to"); }
-static inline QString signatureAttribute() { return QStringLiteral("signature"); }
-static inline QString snippetAttribute() { return QStringLiteral("snippet"); }
-static inline QString snakeCaseAttribute() { return QStringLiteral("snake-case"); }
-static inline QString staticAttribute() { return QStringLiteral("static"); }
-static inline QString classmethodAttribute() { return QStringLiteral("classmethod"); }
-static inline QString threadAttribute() { return QStringLiteral("thread"); }
-static inline QString sourceAttribute() { return QStringLiteral("source"); }
-static inline QString streamAttribute() { return QStringLiteral("stream"); }
-static inline QString privateAttribute() { return QStringLiteral("private"); }
-static inline QString xPathAttribute() { return QStringLiteral("xpath"); }
-static inline QString virtualSlotAttribute() { return QStringLiteral("virtual-slot"); }
-static inline QString visibleAttribute() { return QStringLiteral("visible"); }
-static inline QString enumIdentifiedByValueAttribute() { return QStringLiteral("identified-by-value"); }
-
-static inline QString noAttributeValue() { return QStringLiteral("no"); }
-static inline QString yesAttributeValue() { return QStringLiteral("yes"); }
-static inline QString trueAttributeValue() { return QStringLiteral("true"); }
-static inline QString falseAttributeValue() { return QStringLiteral("false"); }
+constexpr auto allowThreadAttribute = "allow-thread"_L1;
+constexpr auto checkFunctionAttribute = "check-function"_L1;
+constexpr auto copyableAttribute = "copyable"_L1;
+constexpr auto accessAttribute = "access"_L1;
+constexpr auto actionAttribute = "action"_L1;
+constexpr auto quoteAfterLineAttribute = "quote-after-line"_L1;
+constexpr auto quoteBeforeLineAttribute = "quote-before-line"_L1;
+constexpr auto textAttribute = "text"_L1;
+constexpr auto nameAttribute = "name"_L1;
+constexpr auto sinceAttribute = "since"_L1;
+constexpr auto untilAttribute = "until"_L1;
+constexpr auto defaultSuperclassAttribute = "default-superclass"_L1;
+constexpr auto deleteInMainThreadAttribute = "delete-in-main-thread"_L1;
+constexpr auto deprecatedAttribute = "deprecated"_L1;
+constexpr auto disableWrapperAttribute = "disable-wrapper"_L1;
+constexpr auto docFileAttribute = "doc-file"_L1;
+constexpr auto exceptionHandlingAttribute = "exception-handling"_L1;
+constexpr auto extensibleAttribute = "extensible"_L1;
+constexpr auto fileNameAttribute = "file-name"_L1;
+constexpr auto fileAttribute = "file"_L1;
+constexpr auto flagsAttribute = "flags"_L1;
+constexpr auto forceAbstractAttribute = "force-abstract"_L1;
+constexpr auto forceIntegerAttribute = "force-integer"_L1;
+constexpr auto formatAttribute = "format"_L1;
+constexpr auto generateUsingAttribute = "generate-using"_L1;
+constexpr auto generateFunctionsAttribute = "generate-functions"_L1;
+constexpr auto classAttribute = "class"_L1;
+constexpr auto generateAttribute = "generate"_L1;
+constexpr auto generateGetSetDefAttribute = "generate-getsetdef"_L1;
+constexpr auto genericClassAttribute = "generic-class"_L1;
+constexpr auto indexAttribute = "index"_L1;
+constexpr auto invalidateAfterUseAttribute = "invalidate-after-use"_L1;
+constexpr auto isNullAttribute = "isNull"_L1;
+constexpr auto locationAttribute = "location"_L1;
+constexpr auto modifiedTypeAttribute = "modified-type"_L1;
+constexpr auto opaqueContainerAttribute = "opaque-containers"_L1;
+constexpr auto operatorBoolAttribute = "operator-bool"_L1;
+constexpr auto parentManagementAttribute = "parent-management"_L1;
+constexpr auto pyiTypeAttribute = "pyi-type"_L1;
+constexpr auto overloadNumberAttribute = "overload-number"_L1;
+constexpr auto ownershipAttribute = "owner"_L1;
+constexpr auto packageAttribute = "package"_L1;
+constexpr auto polymorphicBaseAttribute = "polymorphic-base"_L1;
+constexpr auto positionAttribute = "position"_L1;
+constexpr auto preferredConversionAttribute = "preferred-conversion"_L1;
+constexpr auto preferredTargetLangTypeAttribute = "preferred-target-lang-type"_L1;
+constexpr auto pythonEnumTypeAttribute = "python-type"_L1;
+constexpr auto pythonOverrideAttribute = "python-override"_L1;
+constexpr auto cppEnumTypeAttribute = "cpp-type"_L1;
+constexpr auto qtMetaObjectFunctionsAttribute = "qt-metaobject"_L1;
+constexpr auto qtMetaTypeAttribute = "qt-register-metatype"_L1;
+constexpr auto removeAttribute = "remove"_L1;
+constexpr auto renameAttribute = "rename"_L1;
+constexpr auto readAttribute = "read"_L1;
+constexpr auto targetLangNameAttribute = "target-lang-name"_L1;
+constexpr auto writeAttribute = "write"_L1;
+constexpr auto opaqueContainerFieldAttribute = "opaque-container"_L1;
+constexpr auto replaceAttribute = "replace"_L1;
+constexpr auto toAttribute = "to"_L1;
+constexpr auto signatureAttribute = "signature"_L1;
+constexpr auto snippetAttribute = "snippet"_L1;
+constexpr auto snakeCaseAttribute = "snake-case"_L1;
+constexpr auto staticAttribute = "static"_L1;
+constexpr auto classmethodAttribute = "classmethod"_L1;
+constexpr auto threadAttribute = "thread"_L1;
+constexpr auto sourceAttribute = "source"_L1;
+constexpr auto streamAttribute = "stream"_L1;
+constexpr auto privateAttribute = "private"_L1;
+constexpr auto xPathAttribute = "xpath"_L1;
+constexpr auto virtualSlotAttribute = "virtual-slot"_L1;
+constexpr auto visibleAttribute = "visible"_L1;
+constexpr auto enumIdentifiedByValueAttribute = "identified-by-value"_L1;
+constexpr auto subModuleOfAttribute = "submodule-of"_L1;
+
+constexpr auto noAttributeValue = "no"_L1;
+constexpr auto yesAttributeValue = "yes"_L1;
+constexpr auto trueAttributeValue = "true"_L1;
+constexpr auto falseAttributeValue = "false"_L1;
static bool isTypeEntry(StackElement el)
{
if (patternIn.startsWith(u'^') && patternIn.endsWith(u'$'))
pattern = patternIn;
else if (patternIn == u"*")
- pattern = QStringLiteral("^.*$");
+ pattern = "^.*$"_L1;
else
pattern = u'^' + QRegularExpression::escape(patternIn) + u'$';
re->setPattern(pattern);
return true;
}
+static inline bool hasFileSnippetAttributes(const QXmlStreamAttributes *attributes)
+{
+ return attributes->hasAttribute(fileAttribute);
+}
+
// Extract a snippet from a file within annotation "// @snippet label".
std::optional<QString>
extractSnippet(const QString &code, const QString &snippetLabel)
{
if (snippetLabel.isEmpty())
return code;
- const QString pattern = QStringLiteral(R"(^\s*//\s*@snippet\s+)")
+ const QString pattern = R"(^\s*//\s*@snippet\s+)"_L1
+ QRegularExpression::escape(snippetLabel)
- + QStringLiteral(R"(\s*$)");
+ + R"(\s*$)"_L1;
const QRegularExpression snippetRe(pattern);
Q_ASSERT(snippetRe.isValid());
return CodeSnipAbstract::fixSpaces(result);
}
-template <class EnumType, Qt::CaseSensitivity cs = Qt::CaseInsensitive>
+template <class EnumType>
struct EnumLookup
{
QStringView name;
EnumType value;
};
-template <class EnumType, Qt::CaseSensitivity cs>
-bool operator==(const EnumLookup<EnumType, cs> &e1, const EnumLookup<EnumType, cs> &e2)
-{
- return e1.name.compare(e2.name, cs) == 0;
-}
-
-template <class EnumType, Qt::CaseSensitivity cs>
-bool operator<(const EnumLookup<EnumType, cs> &e1, const EnumLookup<EnumType, cs> &e2)
-{
- return e1.name.compare(e2.name, cs) < 0;
-}
-
// Helper macros to define lookup functions that take a QStringView needle
// and an optional default return value.
#define ENUM_LOOKUP_BEGIN(EnumType, caseSensitivity, functionName) \
static std::optional<EnumType> functionName(QStringView needle) \
{ \
- using HaystackEntry = EnumLookup<EnumType, caseSensitivity>; \
- static const HaystackEntry haystack[] =
-
-#define ENUM_LOOKUP_LINEAR_SEARCH() \
- const auto end = haystack + sizeof(haystack) / sizeof(haystack[0]); \
- const auto it = std::find(haystack, end, HaystackEntry{needle, {} }); \
+ using HaystackEntry = EnumLookup<EnumType>; \
+ constexpr auto cs = caseSensitivity; \
+ static constexpr HaystackEntry haystack[] =
+
+#define ENUM_LOOKUP_LINEAR_SEARCH \
+ auto pred = [cs, needle](const HaystackEntry &he) { \
+ return he.name.compare(needle, cs) == 0; \
+ }; \
+ auto end = std::cend(haystack); \
+ auto it = std::find_if(std::cbegin(haystack), end, pred); \
if (it != end) \
return it->value; \
- return {}; \
-}
-
-#define ENUM_LOOKUP_BINARY_SEARCH() \
- const auto end = haystack + sizeof(haystack) / sizeof(haystack[0]); \
- const HaystackEntry needleEntry{needle, {} }; \
- const auto lb = std::lower_bound(haystack, end, needleEntry); \
- if (lb != end && *lb == needleEntry) \
- return lb->value; \
- return {}; \
+ return std::nullopt; \
}
ENUM_LOOKUP_BEGIN(TypeSystem::AllowThread, Qt::CaseInsensitive,
{u"no", TypeSystem::AllowThread::Disallow},
{u"false", TypeSystem::AllowThread::Disallow},
};
-ENUM_LOOKUP_LINEAR_SEARCH()
+ENUM_LOOKUP_LINEAR_SEARCH
ENUM_LOOKUP_BEGIN(TypeSystem::BoolCast, Qt::CaseInsensitive,
{u"no", TypeSystem::BoolCast::Disabled},
{u"false", TypeSystem::BoolCast::Disabled},
};
-ENUM_LOOKUP_LINEAR_SEARCH()
+ENUM_LOOKUP_LINEAR_SEARCH
ENUM_LOOKUP_BEGIN(TypeSystem::PythonEnumType, Qt::CaseSensitive,
pythonEnumTypeFromAttribute)
{u"Flag", TypeSystem::PythonEnumType::Flag},
{u"IntFlag", TypeSystem::PythonEnumType::IntFlag},
};
-ENUM_LOOKUP_LINEAR_SEARCH()
+ENUM_LOOKUP_LINEAR_SEARCH
ENUM_LOOKUP_BEGIN(TypeSystem::QtMetaTypeRegistration, Qt::CaseSensitive,
qtMetaTypeFromAttribute)
{u"no", TypeSystem::QtMetaTypeRegistration::Disabled},
{u"false", TypeSystem::QtMetaTypeRegistration::Disabled},
};
-ENUM_LOOKUP_LINEAR_SEARCH()
+ENUM_LOOKUP_LINEAR_SEARCH
ENUM_LOOKUP_BEGIN(TypeSystem::Language, Qt::CaseInsensitive,
languageFromAttribute)
{u"shell", TypeSystem::ShellCode}, // coloca no header, mas antes da declaracao da classe
{u"target", TypeSystem::TargetLangCode} // em algum lugar do cpp
};
-ENUM_LOOKUP_BINARY_SEARCH()
+ENUM_LOOKUP_LINEAR_SEARCH
ENUM_LOOKUP_BEGIN(TypeSystem::Ownership, Qt::CaseInsensitive,
ownershipFromFromAttribute)
{u"c++", TypeSystem::CppOwnership},
{u"default", TypeSystem::DefaultOwnership}
};
-ENUM_LOOKUP_LINEAR_SEARCH()
+ENUM_LOOKUP_LINEAR_SEARCH
ENUM_LOOKUP_BEGIN(AddedFunction::Access, Qt::CaseInsensitive,
addedFunctionAccessFromAttribute)
{u"public", AddedFunction::Public},
{u"protected", AddedFunction::Protected},
};
-ENUM_LOOKUP_LINEAR_SEARCH()
+ENUM_LOOKUP_LINEAR_SEARCH
ENUM_LOOKUP_BEGIN(FunctionModification::ModifierFlag, Qt::CaseSensitive,
modifierFromAttribute)
{u"private", FunctionModification::Private},
{u"public", FunctionModification::Public},
{u"protected", FunctionModification::Protected},
- {u"friendly", FunctionModification::Friendly},
{u"rename", FunctionModification::Rename},
{u"final", FunctionModification::Final},
{u"non-final", FunctionModification::NonFinal}
};
-ENUM_LOOKUP_LINEAR_SEARCH()
+ENUM_LOOKUP_LINEAR_SEARCH
ENUM_LOOKUP_BEGIN(ReferenceCount::Action, Qt::CaseInsensitive,
referenceCountFromAttribute)
{u"set", ReferenceCount::Set},
{u"ignore", ReferenceCount::Ignore}
};
-ENUM_LOOKUP_LINEAR_SEARCH()
+ENUM_LOOKUP_LINEAR_SEARCH
ENUM_LOOKUP_BEGIN(ArgumentOwner::Action, Qt::CaseInsensitive,
argumentOwnerActionFromAttribute)
{u"add", ArgumentOwner::Add},
{u"remove", ArgumentOwner::Remove}
};
-ENUM_LOOKUP_LINEAR_SEARCH()
+ENUM_LOOKUP_LINEAR_SEARCH
ENUM_LOOKUP_BEGIN(TypeSystem::CodeSnipPosition, Qt::CaseInsensitive,
codeSnipPositionFromAttribute)
{
{u"beginning", TypeSystem::CodeSnipPositionBeginning},
{u"end", TypeSystem::CodeSnipPositionEnd},
- {u"declaration", TypeSystem::CodeSnipPositionDeclaration}
+ {u"declaration", TypeSystem::CodeSnipPositionDeclaration},
+ {u"override", TypeSystem::CodeSnipPositionPyOverride}
};
-ENUM_LOOKUP_LINEAR_SEARCH()
+ENUM_LOOKUP_LINEAR_SEARCH
ENUM_LOOKUP_BEGIN(Include::IncludeType, Qt::CaseInsensitive,
locationFromAttribute)
{u"local", Include::LocalPath},
{u"target", Include::TargetLangImport}
};
-ENUM_LOOKUP_LINEAR_SEARCH()
+ENUM_LOOKUP_LINEAR_SEARCH
ENUM_LOOKUP_BEGIN(TypeSystem::DocModificationMode, Qt::CaseInsensitive,
docModificationFromAttribute)
{u"prepend", TypeSystem::DocModificationPrepend},
{u"replace", TypeSystem::DocModificationReplace}
};
-ENUM_LOOKUP_LINEAR_SEARCH()
+ENUM_LOOKUP_LINEAR_SEARCH
ENUM_LOOKUP_BEGIN(ContainerTypeEntry::ContainerKind, Qt::CaseSensitive,
containerTypeFromAttribute)
{u"pair", ContainerTypeEntry::PairContainer},
{u"span", ContainerTypeEntry::SpanContainer}
};
-ENUM_LOOKUP_LINEAR_SEARCH()
+ENUM_LOOKUP_LINEAR_SEARCH
ENUM_LOOKUP_BEGIN(TypeRejection::MatchType, Qt::CaseSensitive,
typeRejectionFromAttribute)
{u"argument-type", TypeRejection::ArgumentType},
{u"return-type", TypeRejection::ReturnType}
};
-ENUM_LOOKUP_LINEAR_SEARCH()
+ENUM_LOOKUP_LINEAR_SEARCH
ENUM_LOOKUP_BEGIN(TypeSystem::ExceptionHandling, Qt::CaseSensitive,
exceptionHandlingFromAttribute)
{u"yes", TypeSystem::ExceptionHandling::On},
{u"true", TypeSystem::ExceptionHandling::On},
};
-ENUM_LOOKUP_LINEAR_SEARCH()
+ENUM_LOOKUP_LINEAR_SEARCH
ENUM_LOOKUP_BEGIN(TypeSystem::SmartPointerType, Qt::CaseSensitive,
smartPointerTypeFromAttribute)
{u"value-handle", TypeSystem::SmartPointerType::ValueHandle},
{u"shared", TypeSystem::SmartPointerType::Shared}
};
-ENUM_LOOKUP_LINEAR_SEARCH()
+ENUM_LOOKUP_LINEAR_SEARCH
template <class EnumType>
static std::optional<EnumType>
{u"true", TypeSystem::SnakeCase::Enabled},
{u"both", TypeSystem::SnakeCase::Both},
};
-ENUM_LOOKUP_LINEAR_SEARCH()
+ENUM_LOOKUP_LINEAR_SEARCH
ENUM_LOOKUP_BEGIN(TypeSystem::Visibility, Qt::CaseSensitive,
visibilityFromAttribute)
{u"yes", TypeSystem::Visibility::Visible},
{u"true", TypeSystem::Visibility::Visible},
};
-ENUM_LOOKUP_LINEAR_SEARCH()
+ENUM_LOOKUP_LINEAR_SEARCH
static int indexOfAttribute(const QXmlStreamAttributes &atts,
- QStringView name)
+ QAnyStringView name)
{
for (qsizetype i = 0, size = atts.size(); i < size; ++i) {
if (atts.at(i).qualifiedName() == name)
}
static QString msgUnimplementedElementWarning(const ConditionalStreamReader &reader,
- QStringView name)
+ QAnyStringView name)
{
QString message;
QTextStream(&message) << "The element \"" << name
static QString
msgUnimplementedAttributeValueWarning(const ConditionalStreamReader &reader,
- QStringView name, QStringView value)
+ QAnyStringView name, QAnyStringView value)
{
QString message;
QTextStream(&message) << "The value \"" << value
static bool addRejection(TypeDatabase *database, bool generate, QXmlStreamAttributes *attributes,
QString *errorMessage)
{
- const auto classIndex = indexOfAttribute(*attributes, classAttribute());
+ const auto classIndex = indexOfAttribute(*attributes, classAttribute);
if (classIndex == -1) {
- *errorMessage = msgMissingAttribute(classAttribute());
+ *errorMessage = msgMissingAttribute(classAttribute);
return false;
}
bool TypeSystemParser::importFileElement(const QXmlStreamAttributes &atts)
{
- const QString fileName = atts.value(nameAttribute()).toString();
+ const QString fileName = atts.value(nameAttribute).toString();
if (fileName.isEmpty()) {
m_error = u"Required attribute 'name' missing for include-file tag."_s;
return false;
}
}
- const auto quoteFrom = atts.value(quoteAfterLineAttribute());
+ const auto quoteFrom = atts.value(quoteAfterLineAttribute);
bool foundFromOk = quoteFrom.isEmpty();
bool from = quoteFrom.isEmpty();
- const auto quoteTo = atts.value(quoteBeforeLineAttribute());
+ const auto quoteTo = atts.value(quoteBeforeLineAttribute);
bool foundToOk = quoteTo.isEmpty();
bool to = true;
}
}
if (!foundFromOk || !foundToOk) {
- QString fromError = QStringLiteral("Could not find quote-after-line='%1' in file '%2'.")
- .arg(quoteFrom.toString(), fileName);
- QString toError = QStringLiteral("Could not find quote-before-line='%1' in file '%2'.")
- .arg(quoteTo.toString(), fileName);
+ QString fromError = QString::fromLatin1("Could not find quote-after-line='%1' in file '%2'.")
+ .arg(quoteFrom.toString(), fileName);
+ QString toError = QString::fromLatin1("Could not find quote-before-line='%1' in file '%2'.")
+ .arg(quoteTo.toString(), fileName);
if (!foundToOk)
m_error = toError;
return true;
}
-static bool convertBoolean(QStringView value, const QString &attributeName, bool defaultValue)
+static bool convertBoolean(QStringView value, QAnyStringView attributeName, bool defaultValue)
{
- if (value.compare(trueAttributeValue(), Qt::CaseInsensitive) == 0
- || value.compare(yesAttributeValue(), Qt::CaseInsensitive) == 0) {
+ if (value.compare(trueAttributeValue, Qt::CaseInsensitive) == 0
+ || value.compare(yesAttributeValue, Qt::CaseInsensitive) == 0) {
return true;
}
- if (value.compare(falseAttributeValue(), Qt::CaseInsensitive) == 0
- || value.compare(noAttributeValue(), Qt::CaseInsensitive) == 0) {
+ if (value.compare(falseAttributeValue, Qt::CaseInsensitive) == 0
+ || value.compare(noAttributeValue, Qt::CaseInsensitive) == 0) {
return false;
}
- const QString warn = QStringLiteral("Boolean value '%1' not supported in attribute '%2'. Use 'yes' or 'no'. Defaulting to '%3'.")
- .arg(value)
- .arg(attributeName,
- defaultValue ? yesAttributeValue() : noAttributeValue());
-
- qCWarning(lcShiboken).noquote().nospace() << warn;
+ qCWarning(lcShiboken).noquote().nospace() << "Boolean value '" << value
+ << "' not supported in attribute '" << attributeName
+ << "'. Use 'yes' or 'no'. Defaulting to '"
+ << (defaultValue ? yesAttributeValue : noAttributeValue) << "'.";
return defaultValue;
}
static bool convertRemovalAttribute(QStringView value)
{
return value == u"all" // Legacy
- || convertBoolean(value, removeAttribute(), false);
+ || convertBoolean(value, removeAttribute, false);
}
// Check whether an entry should be dropped, allowing for dropping the module
static QString checkSignatureError(const QString& signature, const QString& tag)
{
QString funcName = signature.left(signature.indexOf(u'(')).trimmed();
- static const QRegularExpression whiteSpace(QStringLiteral("\\s"));
+ static const QRegularExpression whiteSpace("\\s"_L1);
Q_ASSERT(whiteSpace.isValid());
if (!funcName.startsWith(u"operator ") && funcName.contains(whiteSpace)) {
return QString::fromLatin1("Error in <%1> tag signature attribute '%2'.\n"
auto result = std::make_shared<CustomTypeEntry>(name, since, m_contextStack.top()->entry);
for (auto i = attributes->size() - 1; i >= 0; --i) {
const auto name = attributes->at(i).qualifiedName();
- if (name == checkFunctionAttribute())
+ if (name == checkFunctionAttribute)
result->setCheckFunction(attributes->takeAt(i).value().toString());
}
return result;
const QString targetLangFlagName = lst.join(u'.');
const QString &targetLangQualifier = enumEntry->targetLangQualifier();
if (targetLangFlagName != targetLangQualifier) {
- qCWarning(lcShiboken).noquote().nospace()
- << QStringLiteral("enum %1 and flags %2 (%3) differ in qualifiers")
- .arg(targetLangQualifier, lst.value(0), targetLangFlagName);
+ qCWarning(lcShiboken, "enum %s and flags %s (%s) differ in qualifiers",
+ qPrintable(targetLangQualifier), qPrintable(lst.value(0)),
+ qPrintable(targetLangFlagName));
}
ftype->setFlagsName(name);
return nullptr;
for (auto i = attributes->size() - 1; i >= 0; --i) {
const auto name = attributes->at(i).qualifiedName();
- if (name == targetLangNameAttribute()) {
+ if (name == targetLangNameAttribute) {
type->setTargetLangName(attributes->takeAt(i).value().toString());
} else if (name == u"target-lang-api-name") {
targetLangApiName = attributes->takeAt(i).value().toString();
- } else if (name == preferredConversionAttribute()) {
+ } else if (name == preferredConversionAttribute) {
qCWarning(lcShiboken, "%s",
qPrintable(msgUnimplementedAttributeWarning(reader, name)));
- } else if (name == preferredTargetLangTypeAttribute()) {
+ } else if (name == preferredTargetLangTypeAttribute) {
const bool v = convertBoolean(attributes->takeAt(i).value(),
- preferredTargetLangTypeAttribute(), true);
+ preferredTargetLangTypeAttribute, true);
type->setPreferredTargetLangType(v);
} else if (name == u"default-constructor") {
type->setDefaultConstructor(attributes->takeAt(i).value().toString());
for (auto i = attributes->size() - 1; i >= 0; --i) {
const auto name = attributes->at(i).qualifiedName();
- if (name == opaqueContainerAttribute()) {
+ if (name == opaqueContainerAttribute) {
const auto attribute = attributes->takeAt(i);
OpaqueContainers oc;
if (!parseOpaqueContainers(attribute.value(), &oc))
OpaqueContainers oc;
for (auto i = attributes->size() - 1; i >= 0; --i) {
const auto name = attributes->at(i).qualifiedName();
- if (name == nameAttribute()) {
+ if (name == nameAttribute) {
containerName = attributes->takeAt(i).value().toString();
- } else if (name == opaqueContainerAttribute()) {
+ } else if (name == opaqueContainerAttribute) {
const auto attribute = attributes->takeAt(i);
if (!parseOpaqueContainers(attribute.value(), &oc))
return false;
}
}
if (containerName.isEmpty()) {
- m_error = msgMissingAttribute(nameAttribute());
+ m_error = msgMissingAttribute(nameAttribute);
return false;
}
m_context->opaqueContainerHash[containerName].append(oc);
} else if (name == u"lower-bound") {
qCWarning(lcShiboken, "%s",
qPrintable(msgUnimplementedAttributeWarning(reader, name)));
- } else if (name == forceIntegerAttribute()) {
+ } else if (name == docFileAttribute) {
+ entry->setDocFile(attributes->takeAt(i).value().toString());
+ } else if (name == forceIntegerAttribute) {
qCWarning(lcShiboken, "%s",
qPrintable(msgUnimplementedAttributeWarning(reader, name)));
- } else if (name == pythonEnumTypeAttribute()) {
+ } else if (name == pythonEnumTypeAttribute) {
const auto attribute = attributes->takeAt(i);
const auto typeOpt = pythonEnumTypeFromAttribute(attribute.value());
if (typeOpt.has_value()) {
qCWarning(lcShiboken, "%s",
qPrintable(msgInvalidAttributeValue(attribute)));
}
- } else if (name == cppEnumTypeAttribute()) {
+ } else if (name == cppEnumTypeAttribute) {
entry->setCppType(attributes->takeAt(i).value().toString());
- } else if (name == extensibleAttribute()) {
+ } else if (name == extensibleAttribute) {
qCWarning(lcShiboken, "%s",
qPrintable(msgUnimplementedAttributeWarning(reader, name)));
- } else if (name == flagsAttribute()) {
+ } else if (name == flagsAttribute) {
flagNames = attributes->takeAt(i).value().toString();
}
}
}
result->setExtends(*extendsIt);
attributes->removeAt(i);
- } else if (attributeName == visibleAttribute()) {
+ } else if (attributeName == visibleAttribute) {
const auto attribute = attributes->takeAt(i);
const auto visibilityOpt = visibilityFromAttribute(attribute.value());
if (!visibilityOpt.has_value()) {
return nullptr;
}
visibility = visibilityOpt.value();
- } else if (attributeName == generateAttribute()) {
- if (!convertBoolean(attributes->takeAt(i).value(), generateAttribute(), true))
+ } else if (attributeName == generateAttribute) {
+ if (!convertBoolean(attributes->takeAt(i).value(), generateAttribute, true))
visibility = TypeSystem::Visibility::Invisible;
- } else if (attributeName == generateUsingAttribute()) {
- result->setGenerateUsing(convertBoolean(attributes->takeAt(i).value(), generateUsingAttribute(), true));
+ } else if (attributeName == generateUsingAttribute) {
+ result->setGenerateUsing(convertBoolean(attributes->takeAt(i).value(),
+ generateUsingAttribute, true));
}
}
const bool hasModification = attributes->size() < oldAttributesSize;
QString originalSignature;
+ QString docFile;
for (auto i = attributes->size() - 1; i >= 0; --i) {
const auto name = attributes->at(i).qualifiedName();
- if (name == signatureAttribute())
+ if (name == signatureAttribute)
originalSignature = attributes->takeAt(i).value().toString().simplified();
+ else if (name == docFileAttribute)
+ docFile = attributes->takeAt(i).value().toString();
}
const QString signature = TypeDatabase::normalizedSignature(originalSignature);
if (signature.isEmpty()) {
- m_error = msgMissingAttribute(signatureAttribute());
+ m_error = msgMissingAttribute(signatureAttribute);
return nullptr;
}
if (!existingType) {
auto result = std::make_shared<FunctionTypeEntry>(name, signature, since,
currentParentTypeEntry());
+ result->setTargetLangPackage(m_defaultPackage);
+ result->setDocFile(docFile);
applyCommonAttributes(reader, result, attributes);
return result;
}
if (existingType->type() != TypeEntry::FunctionType) {
- m_error = QStringLiteral("%1 expected to be a function, but isn't! Maybe it was already declared as a class or something else.")
- .arg(name);
+ m_error = name + " expected to be a function, but isn't! Maybe it was already declared as a class or something else."_L1;
return nullptr;
}
m_error = u"typedef entries must be nested in namespaces or type system."_s;
return nullptr;
}
- const auto sourceIndex = indexOfAttribute(*attributes, sourceAttribute());
+ const auto sourceIndex = indexOfAttribute(*attributes, sourceAttribute);
if (sourceIndex == -1) {
- m_error = msgMissingAttribute(sourceAttribute());
+ m_error = msgMissingAttribute(sourceAttribute);
return nullptr;
}
const QString sourceType = attributes->takeAt(sourceIndex).value().toString();
QString package = m_defaultPackage;
for (auto i = attributes->size() - 1; i >= 0; --i) {
const auto name = attributes->at(i).qualifiedName();
- if (name == streamAttribute()) {
- ctype->setStream(convertBoolean(attributes->takeAt(i).value(), streamAttribute(), false));
- } else if (name == privateAttribute()) {
+ if (name == streamAttribute) {
+ ctype->setStream(convertBoolean(attributes->takeAt(i).value(), streamAttribute, false));
+ } else if (name == privateAttribute) {
ctype->setPrivate(convertBoolean(attributes->takeAt(i).value(),
- privateAttribute(), false));
- } else if (name == generateAttribute()) {
- generate = convertBoolean(attributes->takeAt(i).value(), generateAttribute(), true);
- } else if (name ==packageAttribute()) {
+ privateAttribute, false));
+ } else if (name == generateAttribute) {
+ generate = convertBoolean(attributes->takeAt(i).value(), generateAttribute, true);
+ } else if (name ==packageAttribute) {
package = attributes->takeAt(i).value().toString();
- } else if (name == defaultSuperclassAttribute()) {
+ } else if (name == defaultSuperclassAttribute) {
ctype->setDefaultSuperclass(attributes->takeAt(i).value().toString());
- } else if (name == genericClassAttribute()) {
+ } else if (name == genericClassAttribute) {
qCWarning(lcShiboken, "%s",
qPrintable(msgUnimplementedAttributeWarning(reader, name)));
- const bool v = convertBoolean(attributes->takeAt(i).value(), genericClassAttribute(), false);
+ const bool v = convertBoolean(attributes->takeAt(i).value(),
+ genericClassAttribute, false);
ctype->setGenericClass(v);
- } else if (name == targetLangNameAttribute()) {
+ } else if (name == targetLangNameAttribute) {
ctype->setTargetLangName(attributes->takeAt(i).value().toString());
- } else if (name == u"polymorphic-base") {
- ctype->setPolymorphicIdValue(attributes->takeAt(i).value().toString());
+ } else if (name == polymorphicBaseAttribute) {
+ const bool v = convertBoolean(attributes->takeAt(i).value(),
+ polymorphicBaseAttribute, false);
+ ctype->setIsPolymorphicBase(v);
} else if (name == u"polymorphic-name-function") {
ctype->setPolymorphicNameFunction(attributes->takeAt(i).value().toString());
} else if (name == u"polymorphic-id-expression") {
ctype->setPolymorphicIdValue(attributes->takeAt(i).value().toString());
- } else if (name == copyableAttribute()) {
- const bool v = convertBoolean(attributes->takeAt(i).value(), copyableAttribute(), false);
+ } else if (name == copyableAttribute) {
+ const bool v = convertBoolean(attributes->takeAt(i).value(),
+ copyableAttribute, false);
ctype->setCopyable(v ? ComplexTypeEntry::CopyableSet : ComplexTypeEntry::NonCopyableSet);
- } else if (name == exceptionHandlingAttribute()) {
+ } else if (name == exceptionHandlingAttribute) {
const auto attribute = attributes->takeAt(i);
const auto exceptionOpt = exceptionHandlingFromAttribute(attribute.value());
if (exceptionOpt.has_value()) {
qCWarning(lcShiboken, "%s",
qPrintable(msgInvalidAttributeValue(attribute)));
}
- } else if (name == allowThreadAttribute()) {
+ } else if (name == allowThreadAttribute) {
const auto attribute = attributes->takeAt(i);
const auto allowThreadOpt = allowThreadFromAttribute(attribute.value());
if (allowThreadOpt.has_value()) {
qPrintable(msgUnimplementedAttributeWarning(reader, name)));
} else if (name == u"hash-function") {
ctype->setHashFunction(attributes->takeAt(i).value().toString());
- } else if (name == forceAbstractAttribute()) {
- if (convertBoolean(attributes->takeAt(i).value(), forceAbstractAttribute(), false))
+ } else if (name == forceAbstractAttribute) {
+ if (convertBoolean(attributes->takeAt(i).value(), forceAbstractAttribute, false))
ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::ForceAbstract);
- } else if (name == deprecatedAttribute()) {
- if (convertBoolean(attributes->takeAt(i).value(), deprecatedAttribute(), false))
+ } else if (name == deprecatedAttribute) {
+ if (convertBoolean(attributes->takeAt(i).value(), deprecatedAttribute, false))
ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::Deprecated);
- } else if (name == disableWrapperAttribute()) {
- if (convertBoolean(attributes->takeAt(i).value(), disableWrapperAttribute(), false))
+ } else if (name == disableWrapperAttribute) {
+ if (convertBoolean(attributes->takeAt(i).value(), disableWrapperAttribute, false))
ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::DisableWrapper);
- } else if (name == deleteInMainThreadAttribute()) {
- if (convertBoolean(attributes->takeAt(i).value(), deleteInMainThreadAttribute(), false))
+ } else if (name == deleteInMainThreadAttribute) {
+ if (convertBoolean(attributes->takeAt(i).value(), deleteInMainThreadAttribute, false))
ctype->setDeleteInMainThread(true);
- } else if (name == generateFunctionsAttribute()) {
+ } else if (name == qtMetaObjectFunctionsAttribute) {
+ if (!convertBoolean(attributes->takeAt(i).value(),
+ qtMetaObjectFunctionsAttribute, true)) {
+ ctype->setTypeFlags(ctype->typeFlags()
+ | ComplexTypeEntry::DisableQtMetaObjectFunctions);
+ }
+ } else if (name == generateFunctionsAttribute) {
const auto names = attributes->takeAt(i).value();
const auto nameList = names.split(u';', Qt::SkipEmptyParts);
QSet<QString> nameSet;
ctype->setGenerateFunctions(nameSet);
} else if (name == u"target-type") {
ctype->setTargetType(attributes->takeAt(i).value().toString());
- } else if (name == snakeCaseAttribute()) {
+ } else if (name == snakeCaseAttribute) {
const auto attribute = attributes->takeAt(i);
const auto snakeCaseOpt = snakeCaseFromAttribute(attribute.value());
if (snakeCaseOpt.has_value()) {
qCWarning(lcShiboken, "%s",
qPrintable(msgInvalidAttributeValue(attribute)));
}
- } else if (name == isNullAttribute()) {
+ } else if (name == isNullAttribute) {
const auto attribute = attributes->takeAt(i);
const auto boolCastOpt = boolCastFromAttribute(attribute.value());
if (boolCastOpt.has_value()) {
qCWarning(lcShiboken, "%s",
qPrintable(msgInvalidAttributeValue(attribute)));
}
- } else if (name == operatorBoolAttribute()) {
+ } else if (name == operatorBoolAttribute) {
const auto attribute = attributes->takeAt(i);
const auto boolCastOpt = boolCastFromAttribute(attribute.value());
if (boolCastOpt.has_value()) {
qCWarning(lcShiboken, "%s",
qPrintable(msgInvalidAttributeValue(attribute)));
}
- } else if (name == qtMetaTypeAttribute()) {
+ } else if (name == qtMetaTypeAttribute) {
const auto attribute = attributes->takeAt(i);
const auto qtMetaTypeOpt = qtMetaTypeFromAttribute(attribute.value());
if (qtMetaTypeOpt.has_value()) {
qCWarning(lcShiboken, "%s",
qPrintable(msgInvalidAttributeValue(attribute)));
}
- } else if (name == parentManagementAttribute()) {
+ } else if (name == parentManagementAttribute) {
const auto attribute = attributes->takeAt(i);
- if (convertBoolean(attribute.value(), parentManagementAttribute(), false))
+ if (convertBoolean(attribute.value(), parentManagementAttribute, false))
ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::ParentManagement);
ComplexTypeEntry::setParentManagementEnabled(true);
}
QString rename;
for (auto i = attributes->size() - 1; i >= 0; --i) {
const auto name = attributes->at(i).qualifiedName();
- if (name == signatureAttribute()) {
+ if (name == signatureAttribute) {
// Do not remove as it is needed for the type entry later on
signature = attributes->at(i).value().toString().simplified();
- } else if (name == renameAttribute()) {
+ } else if (name == renameAttribute) {
rename = attributes->takeAt(i).value().toString();
}
}
if (signature.isEmpty()) {
- m_error = msgMissingAttribute(signatureAttribute());
+ m_error = msgMissingAttribute(signatureAttribute);
return false;
}
return false;
}
mode = modeOpt.value();
- } else if (name == formatAttribute()) {
+ } else if (name == formatAttribute) {
const auto attribute = attributes->takeAt(i);
const auto langOpt = languageFromAttribute(attribute.value());
if (!langOpt.has_value()) {
QString signature = isTypeEntry(topElement) ? QString() : m_currentSignature;
DocModification mod(mode, signature);
mod.setFormat(lang);
+ if (hasFileSnippetAttributes(attributes)) {
+ const auto snippetOptional = readFileSnippet(attributes);
+ if (!snippetOptional.has_value())
+ return false;
+ mod.setCode(snippetOptional.value().content);
+ }
auto &top = m_contextStack.top();
if (isAddFunction)
top->addedFunctions.last()->addDocModification(mod);
return false;
}
- const auto xpathIndex = indexOfAttribute(*attributes, xPathAttribute());
+ const auto xpathIndex = indexOfAttribute(*attributes, xPathAttribute);
if (xpathIndex == -1) {
- m_error = msgMissingAttribute(xPathAttribute());
+ m_error = msgMissingAttribute(xPathAttribute);
return false;
}
QXmlStreamAttributes *attributes)
{
TypeSystem::SnakeCase snakeCase = TypeSystem::SnakeCase::Unspecified;
+ QString subModuleOf;
+ QString namespaceBegin;
+ QString namespaceEnd;
for (auto i = attributes->size() - 1; i >= 0; --i) {
const auto name = attributes->at(i).qualifiedName();
- if (name == packageAttribute()) {
+ if (name == packageAttribute) {
m_defaultPackage = attributes->takeAt(i).value().toString();
- } else if (name == defaultSuperclassAttribute()) {
+ } else if (name == defaultSuperclassAttribute) {
m_defaultSuperclass = attributes->takeAt(i).value().toString();
- } else if (name == exceptionHandlingAttribute()) {
+ } else if (name == exceptionHandlingAttribute) {
const auto attribute = attributes->takeAt(i);
const auto exceptionOpt = exceptionHandlingFromAttribute(attribute.value());
if (exceptionOpt.has_value()) {
qCWarning(lcShiboken, "%s",
qPrintable(msgInvalidAttributeValue(attribute)));
}
- } else if (name == allowThreadAttribute()) {
+ } else if (name == allowThreadAttribute) {
const auto attribute = attributes->takeAt(i);
const auto allowThreadOpt = allowThreadFromAttribute(attribute.value());
if (allowThreadOpt.has_value()) {
qCWarning(lcShiboken, "%s",
qPrintable(msgInvalidAttributeValue(attribute)));
}
- } else if (name == snakeCaseAttribute()) {
+ } else if (name == snakeCaseAttribute) {
const auto attribute = attributes->takeAt(i);
const auto snakeCaseOpt = snakeCaseFromAttribute(attribute.value());
if (snakeCaseOpt.has_value()) {
qCWarning(lcShiboken, "%s",
qPrintable(msgInvalidAttributeValue(attribute)));
}
+ } else if (name == subModuleOfAttribute) {
+ subModuleOf = attributes->takeAt(i).value().toString();
+ } else if (name == "namespace-begin"_L1) {
+ namespaceBegin = attributes->takeAt(i).value().toString();
+ } else if (name == "namespace-end"_L1) {
+ namespaceEnd = attributes->takeAt(i).value().toString();
}
}
if (add) {
moduleEntry.reset(new TypeSystemTypeEntry(m_defaultPackage, since,
currentParentTypeEntry()));
+ moduleEntry->setSubModule(subModuleOf);
}
moduleEntry->setCodeGeneration(m_generate);
moduleEntry->setSnakeCase(snakeCase);
+ if (!namespaceBegin.isEmpty())
+ moduleEntry->setNamespaceBegin(namespaceBegin);
+ if (!namespaceEnd.isEmpty())
+ moduleEntry->setNamespaceEnd(namespaceEnd);
if ((m_generate == TypeEntry::GenerateForSubclass ||
m_generate == TypeEntry::GenerateNothing) && !m_defaultPackage.isEmpty())
bool generateChild = true;
for (auto i = attributes->size() - 1; i >= 0; --i) {
const auto name = attributes->at(i).qualifiedName();
- if (name == nameAttribute())
+ if (name == nameAttribute)
typeSystemName = attributes->takeAt(i).value().toString();
- else if (name == generateAttribute())
- generateChild = convertBoolean(attributes->takeAt(i).value(), generateAttribute(), true);
+ else if (name == generateAttribute)
+ generateChild = convertBoolean(attributes->takeAt(i).value(), generateAttribute, true);
}
if (typeSystemName.isEmpty()) {
m_error = u"No typesystem name specified"_s;
m_error = u"<reject-enum-value> node must be used inside a <enum-type> node"_s;
return false;
}
- const auto nameIndex = indexOfAttribute(*attributes, nameAttribute());
+ const auto nameIndex = indexOfAttribute(*attributes, nameAttribute);
if (nameIndex == -1) {
- m_error = msgMissingAttribute(nameAttribute());
+ m_error = msgMissingAttribute(nameAttribute);
return false;
}
m_currentEnum->addEnumValueRejection(attributes->takeAt(nameIndex).value().toString());
m_error = u"Type replacement can only be specified for argument modifications"_s;
return false;
}
- const auto modifiedTypeIndex = indexOfAttribute(*attributes, modifiedTypeAttribute());
+ const auto modifiedTypeIndex = indexOfAttribute(*attributes, modifiedTypeAttribute);
if (modifiedTypeIndex == -1) {
m_error = u"Type replacement requires 'modified-type' attribute"_s;
return false;
TypeSystem::Language lang = TypeSystem::NativeCode;
for (auto i = attributes->size() - 1; i >= 0; --i) {
const auto name = attributes->at(i).qualifiedName();
- if (name == classAttribute()) {
+ if (name == classAttribute) {
const auto languageAttribute = attributes->takeAt(i);
const auto langOpt = languageFromAttribute(languageAttribute.value());
if (!langOpt.has_value()) {
lang = langOpt.value();
} else if (name == u"file") {
sourceFile = attributes->takeAt(i).value().toString();
- } else if (name == snippetAttribute()) {
+ } else if (name == snippetAttribute) {
snippetLabel = attributes->takeAt(i).value().toString();
}
}
return false;
}
CodeSnip snip;
- if (!readFileSnippet(attributes, &snip))
+ if (!readCodeSnippet(attributes, &snip))
return false;
m_contextStack.top()->conversionCodeSnips.append(snip);
return true;
QString sourceTypeName;
QString typeCheck;
CodeSnip snip;
- if (!readFileSnippet(attributes, &snip))
+ if (!readCodeSnippet(attributes, &snip))
return false;
const auto &top = m_contextStack.top();
bool ok = false;
*result = index.toInt(&ok);
if (!ok)
- *errorMessage = QStringLiteral("Cannot convert '%1' to integer").arg(index);
+ *errorMessage = QString::fromLatin1("Cannot convert '%1' to integer").arg(index);
return ok;
}
bool resetAfterUse = false;
for (auto i = attributes->size() - 1; i >= 0; --i) {
const auto name = attributes->at(i).qualifiedName();
- if (name == indexAttribute()) {
+ if (name == indexAttribute) {
index = attributes->takeAt(i).value().toString();
- } else if (name == invalidateAfterUseAttribute()) {
+ } else if (name == invalidateAfterUseAttribute) {
resetAfterUse = convertBoolean(attributes->takeAt(i).value(),
- invalidateAfterUseAttribute(), false);
- } else if (name == renameAttribute()) {
+ invalidateAfterUseAttribute, false);
+ } else if (name == renameAttribute) {
renameTo = attributes->takeAt(i).value().toString();
- } else if (name == pyiTypeAttribute()) {
+ } else if (name == pyiTypeAttribute) {
pyiType = attributes->takeAt(i).value().toString();
}
}
if (index.isEmpty()) {
- m_error = msgMissingAttribute(indexAttribute());
+ m_error = msgMissingAttribute(indexAttribute);
return false;
}
std::optional<TypeSystem::Ownership> ownershipOpt;
for (auto i = attributes->size() - 1; i >= 0; --i) {
const auto name = attributes->at(i).qualifiedName();
- if (name == classAttribute()) {
+ if (name == classAttribute) {
const auto classAttribute = attributes->takeAt(i);
const auto langOpt = languageFromAttribute(classAttribute.value());
if (!langOpt.has_value() || langOpt.value() == TypeSystem::ShellCode) {
return false;
}
lang = langOpt.value();
- } else if (name == ownershipAttribute()) {
+ } else if (name == ownershipAttribute) {
const auto attribute = attributes->takeAt(i);
ownershipOpt = ownershipFromFromAttribute(attribute.value());
if (!ownershipOpt.has_value()) {
}
if (!ownershipOpt.has_value()) {
- m_error = QStringLiteral("unspecified ownership");
+ m_error = "unspecified ownership"_L1;
return false;
}
auto &lastArgMod = m_contextStack.top()->functionMods.last().argument_mods().last();
return false;
}
- const auto toIndex = indexOfAttribute(*attributes, toAttribute());
+ const auto toIndex = indexOfAttribute(*attributes, toAttribute);
if (toIndex == -1) {
- m_error = msgMissingAttribute(toAttribute());
+ m_error = msgMissingAttribute(toAttribute);
return false;
}
const QString renamed_to = attributes->takeAt(toIndex).value().toString();
FieldModification fm;
for (auto i = attributes->size() - 1; i >= 0; --i) {
const auto name = attributes->at(i).qualifiedName();
- if (name == nameAttribute()) {
+ if (name == nameAttribute) {
fm.setName(attributes->takeAt(i).value().toString());
- } else if (name == removeAttribute()) {
+ } else if (name == removeAttribute) {
fm.setRemoved(convertRemovalAttribute(attributes->takeAt(i).value()));
- } else if (name == opaqueContainerFieldAttribute()) {
+ } else if (name == opaqueContainerFieldAttribute) {
fm.setOpaqueContainer(convertBoolean(attributes->takeAt(i).value(),
- opaqueContainerFieldAttribute(), false));
- } else if (name == readAttribute()) {
- fm.setReadable(convertBoolean(attributes->takeAt(i).value(), readAttribute(), true));
- } else if (name == writeAttribute()) {
- fm.setWritable(convertBoolean(attributes->takeAt(i).value(), writeAttribute(), true));
- } else if (name == renameAttribute()) {
+ opaqueContainerFieldAttribute, false));
+ } else if (name == readAttribute) {
+ fm.setReadable(convertBoolean(attributes->takeAt(i).value(), readAttribute, true));
+ } else if (name == writeAttribute) {
+ fm.setWritable(convertBoolean(attributes->takeAt(i).value(), writeAttribute, true));
+ } else if (name == renameAttribute) {
fm.setRenamedToName(attributes->takeAt(i).value().toString());
- } else if (name == snakeCaseAttribute()) {
+ } else if (name == snakeCaseAttribute) {
const auto attribute = attributes->takeAt(i);
const auto snakeCaseOpt = snakeCaseFromAttribute(attribute.value());
if (snakeCaseOpt.has_value()) {
}
}
if (fm.name().isEmpty()) {
- m_error = msgMissingAttribute(nameAttribute());
+ m_error = msgMissingAttribute(nameAttribute);
return false;
}
m_contextStack.top()->fieldMods << fm;
QString returnType;
bool staticFunction = false;
bool classMethod = false;
+ bool pythonOverride = false;
QString access;
for (auto i = attributes->size() - 1; i >= 0; --i) {
const auto name = attributes->at(i).qualifiedName();
- if (name == signatureAttribute()) {
+ if (name == signatureAttribute) {
originalSignature = attributes->takeAt(i).value().toString().simplified();
} else if (name == u"return-type") {
returnType = attributes->takeAt(i).value().toString();
- } else if (name == staticAttribute()) {
+ } else if (name == staticAttribute) {
staticFunction = convertBoolean(attributes->takeAt(i).value(),
- staticAttribute(), false);
- } else if (name == classmethodAttribute()) {
+ staticAttribute, false);
+ } else if (name == classmethodAttribute) {
classMethod = convertBoolean(attributes->takeAt(i).value(),
- classmethodAttribute(), false);
- } else if (name == accessAttribute()) {
+ classmethodAttribute, false);
+ } else if (name == accessAttribute) {
access = attributes->takeAt(i).value().toString();
+ } else if (name == pythonOverrideAttribute) {
+ pythonOverride = convertBoolean(attributes->takeAt(i).value(),
+ pythonOverrideAttribute, false);
}
}
func->setStatic(staticFunction);
func->setClassMethod(classMethod);
+ func->setPythonOverride(pythonOverride);
+ func->setTargetLangPackage(m_defaultPackage);
// Create signature for matching modifications
signature = TypeDatabase::normalizedSignature(originalSignature);
TypeSystemPyMethodDefEntry def;
for (auto i = attributes->size() - 1; i >= 0; --i) {
const auto name = attributes->at(i).qualifiedName();
- if (name == nameAttribute()) {
+ if (name == nameAttribute) {
def.name = attributes->takeAt(i).value().toString();
} else if (name == u"doc") {
def.doc = attributes->takeAt(i).value().toString();
TypeSystemProperty property;
for (auto i = attributes->size() - 1; i >= 0; --i) {
const auto name = attributes->at(i).qualifiedName();
- if (name == nameAttribute()) {
+ if (name == nameAttribute) {
property.name = attributes->takeAt(i).value().toString();
} else if (name == u"get") {
property.read = attributes->takeAt(i).value().toString();
property.type = attributes->takeAt(i).value().toString();
} else if (name == u"set") {
property.write = attributes->takeAt(i).value().toString();
- } else if (name == generateGetSetDefAttribute()) {
+ } else if (name == generateGetSetDefAttribute) {
property.generateGetSetDef =
convertBoolean(attributes->takeAt(i).value(),
- generateGetSetDefAttribute(), false);
+ generateGetSetDefAttribute, false);
}
}
if (!property.isValid()) {
{
for (auto i = attributes->size() - 1; i >= 0; --i) {
const auto name = attributes->at(i).qualifiedName();
- if (name == overloadNumberAttribute()) {
+ if (name == overloadNumberAttribute) {
int overloadNumber = TypeSystem::OverloadNumberUnset;
if (!parseOverloadNumber(attributes->takeAt(i), &overloadNumber, &m_error))
return false;
for (auto i = attributes->size() - 1; i >= 0; --i) {
const auto name = attributes->at(i).qualifiedName();
- if (name == allowThreadAttribute()) {
+ if (name == allowThreadAttribute) {
const QXmlStreamAttribute attribute = attributes->takeAt(i);
const auto allowThreadOpt = allowThreadFromAttribute(attribute.value());
if (!allowThreadOpt.has_value()) {
return false;
}
mod->setAllowThread(allowThreadOpt.value());
- } else if (name == exceptionHandlingAttribute()) {
+ } else if (name == exceptionHandlingAttribute) {
const auto attribute = attributes->takeAt(i);
const auto exceptionOpt = exceptionHandlingFromAttribute(attribute.value());
if (!exceptionOpt.has_value()) {
return false;
}
mod->setExceptionHandling(exceptionOpt.value());
- } else if (name == snakeCaseAttribute()) {
+ } else if (name == snakeCaseAttribute) {
const auto attribute = attributes->takeAt(i);
const auto snakeCaseOpt = snakeCaseFromAttribute(attribute.value());
if (!snakeCaseOpt.has_value()) {
return false;
}
mod->setSnakeCase(snakeCaseOpt.value());
- } else if (name == deprecatedAttribute()) {
+ } else if (name == deprecatedAttribute) {
const bool deprecated = convertBoolean(attributes->takeAt(i).value(),
- deprecatedAttribute(), false);
+ deprecatedAttribute, false);
mod->setModifierFlag(deprecated ? FunctionModification::Deprecated
: FunctionModification::Undeprecated);
}
QString rename;
for (auto i = attributes->size() - 1; i >= 0; --i) {
const auto name = attributes->at(i).qualifiedName();
- if (name == signatureAttribute()) {
+ if (name == signatureAttribute) {
originalSignature = attributes->takeAt(i).value().toString().simplified();
- } else if (name == accessAttribute()) {
+ } else if (name == accessAttribute) {
access = attributes->takeAt(i).value().toString();
- } else if (name == renameAttribute()) {
+ } else if (name == renameAttribute) {
rename = attributes->takeAt(i).value().toString();
- } else if (name == removeAttribute()) {
+ } else if (name == removeAttribute) {
removed = convertRemovalAttribute(attributes->takeAt(i).value());
- } else if (name == virtualSlotAttribute() || name == threadAttribute()) {
+ } else if (name == virtualSlotAttribute || name == threadAttribute) {
qCWarning(lcShiboken, "%s",
qPrintable(msgUnimplementedAttributeWarning(reader, name)));
}
if (m == FunctionModification::NonFinal) {
qCWarning(lcShiboken, "%s",
qPrintable(msgUnimplementedAttributeValueWarning(reader,
- accessAttribute(), access)));
+ accessAttribute, access)));
}
mod.setModifierFlag(m);
}
ReferenceCount rc;
for (auto i = attributes->size() - 1; i >= 0; --i) {
const auto name = attributes->at(i).qualifiedName();
- if (name == actionAttribute()) {
+ if (name == actionAttribute) {
const QXmlStreamAttribute attribute = attributes->takeAt(i);
const auto actionOpt = referenceCountFromAttribute(attribute.value());
if (!actionOpt.has_value()) {
ArgumentOwner ao;
for (auto i = attributes->size() - 1; i >= 0; --i) {
const auto name = attributes->at(i).qualifiedName();
- if (name == indexAttribute()) {
+ if (name == indexAttribute) {
const QString index = attributes->takeAt(i).value().toString();
if (!parseArgumentIndex(index, &ao.index, &m_error))
return false;
- } else if (name == actionAttribute()) {
+ } else if (name == actionAttribute) {
const auto action = attributes->takeAt(i);
const auto actionOpt = argumentOwnerActionFromAttribute(action.value());
if (!actionOpt.has_value()) {
return true;
}
-bool TypeSystemParser::readFileSnippet(QXmlStreamAttributes *attributes, CodeSnip *snip)
+std::optional<TypeSystemParser::Snippet>
+ TypeSystemParser::readFileSnippet(QXmlStreamAttributes *attributes)
{
- QString fileName;
- QString snippetLabel;
+ Snippet result;
for (auto i = attributes->size() - 1; i >= 0; --i) {
const auto name = attributes->at(i).qualifiedName();
- if (name == u"file") {
- fileName = attributes->takeAt(i).value().toString();
- } else if (name == snippetAttribute()) {
- snippetLabel = attributes->takeAt(i).value().toString();
+ if (name == fileAttribute) {
+ result.fileName = attributes->takeAt(i).value().toString();
+ } else if (name == snippetAttribute) {
+ result.snippetLabel = attributes->takeAt(i).value().toString();
}
}
- if (fileName.isEmpty())
- return true;
- const QString resolved = m_context->db->modifiedTypesystemFilepath(fileName, m_currentPath);
+ if (result.fileName.isEmpty()) {
+ m_error = "Snippet missing file name"_L1;
+ return std::nullopt;
+ }
+ const QString resolved = m_context->db->modifiedTypesystemFilepath(result.fileName,
+ m_currentPath);
if (!QFile::exists(resolved)) {
m_error = u"File for inject code not exist: "_s
- + QDir::toNativeSeparators(fileName);
- return false;
+ + QDir::toNativeSeparators(result.fileName);
+ return std::nullopt;
}
QFile codeFile(resolved);
if (!codeFile.open(QIODevice::Text | QIODevice::ReadOnly)) {
m_error = msgCannotOpenForReading(codeFile);
- return false;
+ return std::nullopt;
}
- const auto codeOptional = extractSnippet(QString::fromUtf8(codeFile.readAll()), snippetLabel);
+ const auto contentOptional = extractSnippet(QString::fromUtf8(codeFile.readAll()),
+ result.snippetLabel);
codeFile.close();
- if (!codeOptional.has_value()) {
- m_error = msgCannotFindSnippet(resolved, snippetLabel);
- return false;
+ if (!contentOptional.has_value()) {
+ m_error = msgCannotFindSnippet(resolved, result.snippetLabel);
+ return std::nullopt;
}
+ result.content = contentOptional.value();
+ return result;
+}
+
+bool TypeSystemParser::readCodeSnippet(QXmlStreamAttributes *attributes, CodeSnip *snip)
+{
+ if (!hasFileSnippetAttributes(attributes))
+ return true; // Expecting inline content.
+ const auto snippetOptional = readFileSnippet(attributes);
+ if (!snippetOptional.has_value())
+ return false;
+ const auto snippet = snippetOptional.value();
- QString source = fileName;
- if (!snippetLabel.isEmpty())
- source += u" ("_s + snippetLabel + u')';
+ QString source = snippet.fileName;
+ if (!snippet.snippetLabel.isEmpty())
+ source += " ("_L1 + snippet.snippetLabel + u')';
QString content;
QTextStream str(&content);
str << "// ========================================================================\n"
"// START of custom code block [file: "
- << source << "]\n" << codeOptional.value()
+ << source << "]\n" << snippet.content
<< "// END of custom code block [file: " << source
<< "]\n// ========================================================================\n";
snip->addCode(content);
TypeSystem::CodeSnipPosition position = TypeSystem::CodeSnipPositionBeginning;
TypeSystem::Language lang = TypeSystem::TargetLangCode;
CodeSnip snip;
- if (!readFileSnippet(attributes, &snip))
+ if (!readCodeSnippet(attributes, &snip))
return false;
for (auto i = attributes->size() - 1; i >= 0; --i) {
const auto name = attributes->at(i).qualifiedName();
- if (name == classAttribute()) {
+ if (name == classAttribute) {
const auto attribute = attributes->takeAt(i);
const auto langOpt = languageFromAttribute(attribute.value());
if (!langOpt.has_value()) {
return false;
}
lang = langOpt.value();
- } else if (name == positionAttribute()) {
+ } else if (name == positionAttribute) {
const auto attribute = attributes->takeAt(i);
const auto positionOpt = codeSnipPositionFromAttribute(attribute.value());
if (!positionOpt.has_value()) {
Include::IncludeType location = Include::IncludePath;
for (auto i = attributes->size() - 1; i >= 0; --i) {
const auto name = attributes->at(i).qualifiedName();
- if (name == fileNameAttribute()) {
+ if (name == fileNameAttribute) {
fileName = attributes->takeAt(i).value().toString();
- } else if (name == locationAttribute()) {
+ } else if (name == locationAttribute) {
const auto attribute = attributes->takeAt(i);
const auto locationOpt = locationFromAttribute(attribute.value());
if (!locationOpt.has_value()) {
bool TypeSystemParser::parseSystemInclude(const ConditionalStreamReader &,
QXmlStreamAttributes *attributes)
{
- const auto index = indexOfAttribute(*attributes, fileNameAttribute());
+ const auto index = indexOfAttribute(*attributes, fileNameAttribute);
if (index == -1) {
- m_error = msgMissingAttribute(fileNameAttribute());
+ m_error = msgMissingAttribute(fileNameAttribute);
return false;
}
TypeDatabase::instance()->addForceProcessSystemInclude(attributes->takeAt(index).value().toString());
"conversion-rule, native-to-target or add-conversion tags."_s;
return nullptr;
}
- const auto nameIndex = indexOfAttribute(*attributes, nameAttribute());
+ const auto nameIndex = indexOfAttribute(*attributes, nameAttribute);
if (nameIndex == -1) {
- m_error = msgMissingAttribute(nameAttribute());
+ m_error = msgMissingAttribute(nameAttribute);
return nullptr;
}
return new TemplateInstance(attributes->takeAt(nameIndex).value().toString());
const auto name = attributes->at(i).qualifiedName();
if (name == u"from")
from = attributes->takeAt(i).value().toString();
- else if (name == toAttribute())
+ else if (name == toAttribute)
to = attributes->takeAt(i).value().toString();
}
m_templateInstance->addReplaceRule(from, to);
VersionRange versionRange;
for (auto i = attributes.size() - 1; i >= 0; --i) {
const auto name = attributes.at(i).qualifiedName();
- if (name == sinceAttribute()) {
+ if (name == sinceAttribute) {
if (!parseVersion(attributes.takeAt(i).value().toString(),
m_defaultPackage, &versionRange.since, &m_error)) {
return false;
}
- } else if (name == untilAttribute()) {
+ } else if (name == untilAttribute) {
if (!parseVersion(attributes.takeAt(i).value().toString(),
m_defaultPackage, &versionRange.until, &m_error)) {
return false;
if (isTypeEntry(element)) {
QString name;
if (element != StackElement::FunctionTypeEntry) {
- const auto nameIndex = indexOfAttribute(attributes, nameAttribute());
+ const auto nameIndex = indexOfAttribute(attributes, nameAttribute);
if (nameIndex != -1) {
name = attributes.takeAt(nameIndex).value().toString();
} else if (element != StackElement::EnumTypeEntry) { // anonymous enum?
- m_error = msgMissingAttribute(nameAttribute());
+ m_error = msgMissingAttribute(nameAttribute);
return false;
}
}
if (m_context->db->hasDroppedTypeEntries()) {
const QString identifier = element == StackElement::FunctionTypeEntry
- ? attributes.value(signatureAttribute()).toString().simplified() : name;
+ ? attributes.value(signatureAttribute).toString().simplified() : name;
if (shouldDropTypeEntry(m_context->db, m_contextStack, identifier)) {
m_currentDroppedEntryDepth = 1;
if (ReportHandler::isDebug(ReportHandler::SparseDebug)) {
if (element == StackElement::EnumTypeEntry) {
const auto enumIdentifiedByIndex =
- indexOfAttribute(attributes, enumIdentifiedByValueAttribute());
+ indexOfAttribute(attributes, enumIdentifiedByValueAttribute);
const QString identifiedByValue = enumIdentifiedByIndex != -1
? attributes.takeAt(enumIdentifiedByIndex).value().toString() : QString();
if (name.isEmpty()) {
const auto topParent = m_stack.value(m_stack.size() - 3, StackElement::None);
if (isTypeEntry(topParent)) {
- const auto replaceIndex = indexOfAttribute(attributes, replaceAttribute());
+ const auto replaceIndex = indexOfAttribute(attributes, replaceAttribute);
const bool replace = replaceIndex == -1
|| convertBoolean(attributes.takeAt(replaceIndex).value(),
- replaceAttribute(), true);
+ replaceAttribute, true);
auto customConversion = CustomConversion::getCustomConversion(top->entry);
if (!customConversion) {
m_error = msgMissingCustomConversion(top->entry);
return false;
break;
case StackElement::SuppressedWarning: {
- const auto textIndex = indexOfAttribute(attributes, textAttribute());
+ const auto textIndex = indexOfAttribute(attributes, textAttribute);
if (textIndex == -1) {
qCWarning(lcShiboken) << "Suppressed warning with no text specified";
} else {
return false;
break;
case StackElement::Template: {
- const auto nameIndex = indexOfAttribute(attributes, nameAttribute());
+ const auto nameIndex = indexOfAttribute(attributes, nameAttribute);
if (nameIndex == -1) {
- m_error = msgMissingAttribute(nameAttribute());
+ m_error = msgMissingAttribute(nameAttribute);
return false;
}
m_templateEntry.reset(new TemplateEntry(attributes.takeAt(nameIndex).value().toString()));
#include <QtCore/QScopedPointer>
#include <memory>
+#include <optional>
QT_FORWARD_DECLARE_CLASS(QVersionNumber)
QT_FORWARD_DECLARE_CLASS(QXmlStreamAttributes)
QString errorString() const { return m_error; }
private:
+ struct Snippet
+ {
+ QString content;
+ QString fileName;
+ QString snippetLabel;
+ };
+
bool parseXml(ConditionalStreamReader &reader);
bool setupSmartPointerInstantiations();
bool startElement(const ConditionalStreamReader &reader, StackElement element);
QXmlStreamAttributes *);
bool parseParentOwner(const ConditionalStreamReader &, StackElement topElement,
QXmlStreamAttributes *);
- bool readFileSnippet(QXmlStreamAttributes *attributes, CodeSnip *snip);
+ std::optional<Snippet> readFileSnippet(QXmlStreamAttributes *attributes);
+ bool readCodeSnippet(QXmlStreamAttributes *attributes, CodeSnip *snip);
bool parseInjectCode(const ConditionalStreamReader &, StackElement topElement, QXmlStreamAttributes *);
bool parseInclude(const ConditionalStreamReader &, StackElement topElement,
const TypeEntryPtr &entry, QXmlStreamAttributes *);
#define TYPESYSTEMTYPEENTRY_H
#include "typesystem.h"
+#include "modifications_typedefs.h"
#include "typesystem_enums.h"
#include "typesystem_typedefs.h"
CodeSnipList &codeSnips();
void addCodeSnip(const CodeSnip &codeSnip);
+ QString subModuleOf() const;
+ void setSubModule(const QString &);
+
+ const QString &namespaceBegin() const;
+ void setNamespaceBegin(const QString &n);
+
+ const QString &namespaceEnd() const;
+ void setNamespaceEnd(const QString &n);
+
protected:
explicit TypeSystemTypeEntry(TypeEntryPrivate *d);
};
std::shared_ptr<XQuery> libXml_createXQuery(const QString &focus, QString *errorMessage)
{
- XmlDocUniquePtr doc(xmlParseFile(QFile::encodeName(focus).constData()));
+ XmlDocUniquePtr doc(xmlReadFile(QFile::encodeName(focus).constData(),
+ "utf-8", XML_PARSE_NOENT));
if (!doc) {
*errorMessage = u"libxml2: Cannot set focus to "_s + QDir::toNativeSeparators(focus);
return {};
// XSLT transformation
-static const char xsltPrefix[] = R"(<?xml version="1.0" encoding="UTF-8" ?>
+static constexpr auto xsltPrefix = R"(<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
-)";
+)"_L1;
QString libXslt_transform(const QString &xml, QString xsl, QString *errorMessage)
{
ensureInitialized();
// Read XML data
if (!xsl.startsWith(u"<?xml")) {
- xsl.prepend(QLatin1StringView(xsltPrefix));
+ xsl.prepend(xsltPrefix);
xsl.append(u"</xsl:transform>"_s);
}
const QByteArray xmlData = xml.toUtf8();
- XmlDocUniquePtr xmlDoc(xmlParseMemory(xmlData.constData(), xmlData.size()));
+
+ XmlDocUniquePtr xmlDoc(xmlReadMemory(xmlData.constData(), int(xmlData.size()),
+ "", "utf-8", XML_PARSE_NOENT));
if (!xmlDoc) {
*errorMessage = u"xmlParseMemory() failed for XML."_s;
return xml;
# it is unable to set Python_CONFIG i.e. find `python3-config` script
# This workaround sets the Python_SOABI manually for this platform.
if(CMAKE_SYSTEM_NAME STREQUAL "Android" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "armv7-a")
- set(Python_SOABI "cpython-310}")
+ set(Python_SOABI "cpython-311}")
endif()
if(NOT Python_SOABI)
message(FATAL_ERROR "Python_SOABI variable is empty.")
libs = r'${Python_LIBRARIES}'
libs = libs.split(';')
for lib in libs:
- if '\\\\' in lib and Path(lib).is_file():
+ if ('\\\\' in lib or '/' in lib) and Path(lib).is_file():
lib = Path(lib)
prefix = lib.parent
py = lib.name
+.text-center {
+ text-align: center !important;
+}
+
+.text-center img {
+ padding-top: 10px;
+ height: 70px !important;
+}
+
+.cover-img img {
+ object-fit: cover;
+ height: 50%;
+}
+
/* Tables */
.section .docutils.container td {
float:left;
position:relative;
overflow:visible
}
+
+/* We cannot put a :download:`....` command inside
+ * a sphinx-design button, so we add some properties from the button
+ * to the download class to mimic it */
+code.download {
+ text-align: center;
+ color: var(--color-brand-primary);
+ display: block;
+ border-color: transparent;
+ background-color: transparent;
+ border: 1px solid var(--color-brand-primary) !important;
+ border-radius: 0.25rem;
+ font-size: 1rem;
+ font-weight: 400;
+ vertical-align: middle;
+ padding: .375rem .75rem;
+ user-select: none;
+ line-height: 1.5;
+ transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out;
+}
+
+code.download:hover {
+ color: white;
+ background-color: var(--color-brand-primary);
+ border-color: var(--color-brand-primary);
+ text-decoration: none;
+ padding: .375rem .75rem;
+}
+
+dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple):first-child > dt {
+ font-size: +2.25rem;
+ font-weight: 700;
+ color: #ff00dd;
+}
+
+.theme-toggle svg{
+ width: +1.25rem;
+ height: +2.25rem;
+}
+
+.sd-card-title code span {
+ font-size: +1rem;
+ color: var(--color-brand-primary);
+}
:link: ../../examples/example_samplebinding_samplebinding.html
:img-top: ../images/icecream.png
+ .. grid-item-card:: Scriptable Application
+ :class-item: cover-img
+ :link: ../../examples/example_scriptableapplication_scriptableapplication.html
+ :img-top: ../../../_images/example_no_image.png
+
+ .. grid-item-card:: Widget Binding
+ :class-item: cover-img
+ :link: ../../examples/example_widgetbinding_widgetbinding.html
+ :img-top: ../../../_images/example_no_image.png
Examples
+ .. grid-item-card::
+ :class-item: text-center
+
+ Generating Python stub files.
+ +++
+ .. button-ref:: shiboken-genpyi
+ :color: primary
+ :outline:
+ :expand:
+
+ shiboken6-genpyi
+
.. grid-item-card::
:class-item: text-center
shibokenmodule.rst
typesystem.rst
examples/index.rst
+ shiboken-genpyi.rst
considerations.rst
--- /dev/null
+.. _shiboken6-genpyi:
+
+shiboken6-genpyi
+================
+
+`shiboken6-genpyi` is a command line tool to generate Python stub files
+(.pyi) for any shiboken binding-based module (not just PySide). Stub
+files define signatures of all classes, methods (including overloads),
+constants and enums of a module. Signatures also contain type hints.
+This helps your module integrate with Python type checkers and IDEs.
+For example, if you use any function from your module, your IDE's
+function lookup feature will show you the function signature and its
+parameters and return value including types.
+
+
+Usage
+-----
+
+To generate stub files for a module, run the following command:
+
+.. code-block:: bash
+
+ shiboken6-genpyi <module_names> [OPTIONS]
+
+where `<module_names>` is a space-separated list of module names (the
+modules must be importable from the working directory) and where
+`[OPTIONS]` can be one of the following:
+
+* **--quiet**: Run the tool quietly without output to stdout.
+* **--outpath <output_dir>**: Specify the output directory for the
+ generated stub files. If not specified, the stub files are generated
+ in the location of the module binary.
This method should be used **only** for debug purposes by developers.
+ .. function:: dumpTypeGraph(file_name)
+
+ Dumps the inheritance graph of the types existing in libshiboken
+ to ``.dot`` file for use with `Graphviz <https://graphviz.org/>`_.
+
+.. function:: dumpWrapperMap()
+
+ Dumps the map of wrappers existing in libshiboken to standard error.
+
.. py:class:: VoidPtr(address, size = -1, writeable = 0)
:param address: (PyBuffer, SbkObject, int, VoidPtr)
--- /dev/null
+.. _typediscovery:
+
+**************
+Type Discovery
+**************
+
+When converting objects which are part of a class hierarchy from a pointer to a
+base class, it is expected to get the Python type of the actual, most derived
+type, as opposed to C++ which requires a cast for this:
+
+.. code-block:: python
+
+ def event(self, event):
+ if event.type() == QEvent.Type.MousePress:
+ self.do_things(event.position())
+ ...
+
+
+.. code-block:: c++
+
+ bool event(QEvent *event) override
+ {
+ if (event->type() == QEvent::MousePress) {
+ auto *mouseEvent = static_cast<QMouseEvent *>(event);
+ doThings(mouseEvent->position());
+ ...
+ }
+
+The process of determining the type of the event is called `type discovery`.
+
+Shiboken generates code to automatically detect the type. First, it tries to
+find a converter for the name obtained by ``typeid(*pointer).name()``. This
+should normally work as this name is registered by the binding. If that fails,
+it starts walking a type inheritance graph built up in libshiboken to find the
+most derived class by using a cast function (``dynamic_cast<>`` by default) to
+check.
+
+For normal class hierarchies with virtual destructors, no special handling
+is required since ``typeid()`` usually detects the proper class name.
+
+Multiple inheritance
+====================
+
+In case of multiple inheritance in C++, the conversion to the derived class is
+not done in case it is not a single-line direct inheritance. For example, in
+Qt, the class ``QWidget`` inherits both ``QObject`` (base of the ``QObject``
+hierarchy) and ``QPaintDevice``.
+
+When calling a function returning a ``QPaintDevice *``, for example
+``QPainter.device()``, a Python type representing ``QPaintDevice`` is returned
+instead of the underlying widget type. This restriction exists because the
+underlying pointer in C++ is a pointer to a ``QPaintDevice *`` and differs from
+the pointer to the ``QWidget``.
+
+Hierarchies of classes with non-virtual destructors
+===================================================
+
+There are some hierarchies of value-ish C++ classes that do not have virtual
+destructors. This makes type discovery based on ``typeid()`` and
+``dynamic_cast<>`` impossible.
+
+Examples in Qt are the ``QStyleOption``-derived or the ``QGradient``
+-derived classes.
+
+For such classes, some attributes need to be specified on the type entries:
+
+Primarily, a :ref:`polymorphic-id-expression` attribute
+must be specified to be used as a check replacing ``dynamic_cast<>``.
+
+In addition, a :ref:`polymorphic-name-function` attribute can be specified.
+This replaces the type name guess obtained by ``typeid()`` and is mainly a hint
+to speed things up by skipping the checks for each type in the inheritance
+graph.
+
+A :ref:`polymorphic-base` attribute identifies the base class of a hierarchy.
+It should be given in case the base class inherits from another class to
+prevent the logic from going below the base class.
+
+Using type discovery attributes for class hierarchies with virtual destructors
+==============================================================================
+
+It is possible to use :ref:`polymorphic-id-expression` and
+:ref:`polymorphic-name-function` for normal class hierarchies with virtual
+destructors as well since they basically replace ``typeid()`` and
+``dynamic_cast<>``. This makes sense if expressions can be specified that are
+faster than the checks on virtual tables.
+
+Specifying :ref:`polymorphic-base` can also make sense for generating special
+cast functions in case of multiple inheritance. For example, in Qt,
+``QWindow``, ``QLayout``, ``QWidget`` are base classes of hierarchies. Since
+they all inherit from ``QObject``, indicating the base classes prevents
+the logic from using ``QObject`` as a base class.
+
+.. _typediscovery-attributes:
+
+Type discovery attributes reference
+===================================
+
+The following attributes related to type discovery may be be specified on the
+:ref:`object-type` or :ref:`value-type` elements:
+
+.. _polymorphic-id-expression:
+
+polymorphic-id-expression
++++++++++++++++++++++++++
+
+The **polymorphic-id-expression** attribute specifies an expression checking
+whether a base class pointer is of the matching type. For example, in a
+``virtual eventHandler(BaseEvent *e)`` function, this is used to construct a
+Python wrapper matching the derived class (for example, a ``MouseEvent`` or
+similar). The attribute value may contain placeholders:
+
+%1
+ Fully qualified class name
+
+%B
+ Fully qualified name of the base class (found by base class
+ search or as indicated by **polymorphic-base**).
+
+To check for a class inheriting ``BaseEvent``, specify:
+
+.. code-block:: xml
+
+ <object-type name="MouseEvent"
+ polymorphic-id-expression="%B->type() == BaseEvent::MouseEvent"/>
+
+.. _polymorphic-name-function:
+
+polymorphic-name-function
++++++++++++++++++++++++++
+
+The **polymorphic-name-function** attribute specifies the name of a function
+returning the type name of a derived class on the base class type entry.
+Normally, ``typeid(ptr).name()`` is used for this.
+
+The function is expected to return ``const char *``.
+
+.. _polymorphic-base:
+
+polymorphic-base
+++++++++++++++++
+
+The boolean **polymorphic-base** attribute indicates whether the class is the
+base class of a class hierarchy. It is used for the *%B* placeholder in
+**polymorphic-id-expression** and for cast operations in multiple inheritance.
typesystem_solving_compilation.rst
typesystem_specialfunctions.rst
+ typediscovery.rst
| |shell |declaration|Used only for virtual functions. This code is injected at the |
| | | |top. |
| | +-----------+--------------------------------------------------------------+
+| | |override |Used only for virtual functions. The code is injected before |
+| | | |the code calling the Python override. |
+| | +-----------+--------------------------------------------------------------+
| | |beginning |Used only for virtual functions. The code is injected when the|
| | | |function does not has a Python implementation, then the code |
| | | |is inserted before c++ call |
**%CHECKTYPE[CPPTYPE]**
Replaced by a |project| type checking function for a Python variable.
The C++ type is indicated by ``CPPTYPE``.
-
-
-.. _oldconverters:
-
-Converting The Old Converters
-=============================
-
-If you use |project| for your bindings, and has defined some type conversions
-using the ``Shiboken::Converter`` template, then you must update your converters
-to the new scheme.
-
-Previously your conversion rules were declared in one line, like this:
-
-
-.. code-block:: xml
-
- <primitive-type name="Complex" target-lang-api-name="PyComplex">
- <include file-name="complex.h" location="global"/>
- <conversion-rule file="complex_conversions.h"/>
- </primitive-type>
-
-
-And implemented in a separate C++ file, like this:
-
-
-.. code-block:: c++
-
- namespace Shiboken {
- template<> struct Converter<Complex>
- {
- static inline bool checkType(PyObject* pyObj) {
- return PyComplex_Check(pyObj);
- }
- static inline bool isConvertible(PyObject* pyObj) {
- return PyComplex_Check(pyObj);
- }
- static inline PyObject* toPython(void* cppobj) {
- return toPython(*reinterpret_cast<Complex*>(cppobj));
- }
- static inline PyObject* toPython(const Complex& cpx) {
- return PyComplex_FromDoubles(cpx.real(), cpx.imag());
- }
- static inline Complex toCpp(PyObject* pyobj) {
- double real = PyComplex_RealAsDouble(pyobj);
- double imag = PyComplex_ImagAsDouble(pyobj);
- return Complex(real, imag);
- }
- };
- }
-
-
-In this case, the parts of the implementation that will be used in the new
-conversion-rule are the ones in the two last method
-``static inline PyObject* toPython(const Complex& cpx)`` and
-``static inline Complex toCpp(PyObject* pyobj)``. The ``isConvertible`` method
-is gone, and the ``checkType`` is now an attribute of the :ref:`add-conversion <add-conversion>`
-tag. Refer back to the first example in this page and you will be able to
-correlate the above template with the new scheme of conversion rule definition.
.. code-block:: xml
<value-type>
- <inject-documentation mode="append | prepend | replace" format="native | target" >
+ <inject-documentation mode="append | prepend | replace" format="native | target"
+ file="[file]" snippet="[label]">
// the documentation
</inject-code>
</value-type>
* native: Before XML<->Backend transformation occur, so the injected code *must* be a valid XML.
* target: After XML<->Backend transformation occur, so the injected code *must* be a valid backend format.
+The optional ``file`` attribute specifies the file name
+(see :ref:`external-snippets`).
+
+The optional ``snippet`` attribute specifies the snippet label
+(see :ref:`external-snippets`).
+
At the moment the only supported backend is Sphinx.
+If the injected documentation contains a Sphinx function directive, no
+directive will be auto-generated. This can be used to add parameter
+documentation to added functions.
+
modify-documentation
^^^^^^^^^^^^^^^^^^^^
Using Snippets From External Files
==================================
-Code snippets can also be retrieved from external files found in the
-typesystem search path (see :ref:`typesystem-paths`).
+Code or documentation snippets can also be retrieved from external
+files found in the typesystem search path (see :ref:`typesystem-paths`).
.. code-block:: xml
access="public | protected"
overload-number="number"
static="yes | no" classmethod="yes | no"
+ python-override ="yes | no"
since="..."/>
</object-type>
See :ref:`sequence-protocol` for adding the respective functions.
+The *optional* attribute ``python-override`` indicates a special type
+of added function, a python-override that will be generated into
+the native wrapper (see :ref:`modifying-virtual-functions`).
+
.. _declare-function:
declare-function
for building several configurations from one generated source tree,
but still requires listing the correct source files in the
``CMakeLists.txt`` file.
+
+.. _modifying-virtual-functions:
+
+Modifying virtual functions
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Some C++ virtual functions are unsuitable for Python bindings:
+
+.. code-block:: c
+
+ virtual void getInt(int *result) const;
+
+In that case, you would modify it to return the integer instead (or a tuple
+in case of several out-parameters):
+
+.. code-block:: c
+
+ virtual int getInt() const;
+
+For the binding itself, use the common argument modifications (removing
+arguments, modifying return types with injected code snippets) to modify the
+signature.
+
+To make it possible to reimplement the function in Python with the modified
+signature, add a ``python-override`` function with that signature, using an
+arbitrary name for disambiguation:
+
+.. code-block:: xml
+
+ <add-function signature="getIntPyOverride()"
+ return-type="int" python-override="true"/>
+
+This causes a static function performing the call into Python for the override
+to be generated into the native wrapper.
+
+In the existing virtual function, inject a code snippet at the ``shell`` /
+``override`` position which calls the newly added function. The first 2
+arguments are the `Global interpreter lock handle` (``Shiboken::GilState``) and
+the Python method determined by the override check (``PyObject *``). The
+snippet then converts the arguments and return values and returns after that:
+
+.. code-block:: xml
+
+ <modify-function signature="getInt(int*)const">
+ <inject-code class="shell" position="override">
+ *result = getIntPyOverride(gil, pyOverride.object());
+ return;
+ </inject-code>
+ </modify-function>
.. code-block:: xml
- <typesystem package="..." default-superclass="..." allow-thread="..."
- exception-handling="..." snake-case="yes | no | both" >
+ <typesystem package="..."
+ submodule-of="..."
+ allow-thread="..."
+ exception-handling="..."
+ snake-case="yes | no | both"
+ namespace-begin="..."
+ namespace-end="..." >
</typesystem>
The **package** attribute is a string describing the package to be used,
e.g. "QtCore".
-The *optional* **default-superclass** attribute is the canonical C++ base class
-name of all objects, e.g., "object".
+
+The *optional* **submodule-of** attribute specifies the name of a module to
+which the module is added as a sub-module. This requires adapting the
+installation directory of the module accordingly.
The *optional* attributes **allow-thread** and **exception-handling**
specify the default handling for the corresponding function modification
exist (as is the case for example for ``QFileInfo::exists()``),
the snake case name must be used.
+The *optional* **namespace-begin** and **namespace-end** attributes will be
+generated around the forward declarations in the module header. This is
+intended for libraries which can optionally use inline namespaces
+to allow for linking several versions of them together.
+For example, for *Qt*, one would specify ``QT_BEGIN_NAMESPACE``,
+``QT_END_NAMESPACE``, respectively.
+
.. _load-typesystem:
load-typesystem
until="..."
target-lang-api-name="..."
default-constructor="..."
- preferred-conversion="yes | no" />
- view-on="..."
+ preferred-conversion="yes | no"
+ view-on="..." />
</typesystem>
The **name** attribute is the name of the primitive in C++.
flags="yes | no"
flags-revision="..."
cpp-type = "..."
+ doc-file = "..."
python-type = "IntEnum | IntFlag"
lower-bound="..."
upper-bound="..."
casting values. This can be useful for large values triggering MSVC
signedness issues.
+The *optional* **doc-file** attribute specifies the base name of the C++ or
+``.qdoc`` file indicated by ``\relates`` or ``\headerfile`` in ``qdoc``, to
+which the documentation of the enumeration is to be added and displayed in the
+case its a global enumeration. This attribute is for ``qdoc`` only.
+
The **revision** attribute can be used to specify a revision for each type, easing the
production of ABI compatible bindings.
parent-management="yes | no"
polymorphic-id-expression="..."
polymorphic-name-function="..."
+ polymorphic-base="yes | no"
private="yes | no"
+ qt-metaobject="yes | no"
qt-register-metatype = "yes | no | base"
stream="yes | no"
revision="..."
For the *optional* **private** attribute, see :ref:`private_types`.
+The *optional* **qt-metaobject** attribute specifies whether
+the special Qt virtual functions ``metaObject()``,
+``metaCall()``, and ``metaCast()`` are generated. For classes
+using dynamic meta objects, for example, ``QDBusInterface``,
+it can be turned off.
+
The *optional* **qt-register-metatype** attribute determines whether
a Qt meta type registration is generated for ``name *``. By
default, this is only generated for non-QObject types for usage
to all classes. In shiboken 7, it will be mandatory to set the
attribute.
-The *optional* **polymorphic-id-expression** attribute specifies an
-expression checking whether a base class pointer is of the matching
-type. For example, in a ``virtual eventHandler(BaseEvent *e)``
-function, this is used to construct a Python wrapper matching
-the derived class (for example, a ``MouseEvent`` or similar).
-
-The *optional* **polymorphic-name-function** specifies the name of a
-function returning the type name of a derived class on the base class
-type entry. Normally, ``typeid(ptr).name()`` is used for this.
-However, this fails if the type hierarchy does not have virtual functions.
-In this case, a function is required which typically decides depending
-on some type enumeration.
+For the *optional* **polymorphic-id-expression**, **polymorphic-name-function**
+and **polymorphic-base** attributes, see :ref:`typediscovery-attributes`.
interface-type
^^^^^^^^^^^^^^
<typesystem>
<function signature="..." rename="..." since="..."
allow-thread="true | auto | false"
+ doc-file = "..."
exception-handling="off | auto-off | auto-on | on"
overload-number="number"
snake-case="yes | no | both" />
The *optional* **rename** attribute is used to modify the function name.
+The *optional* **doc-file** attribute specifies the base name of the C++ or
+``.qdoc`` file indicated by ``\relates`` or ``\headerfile`` in ``qdoc``, to
+which the documentation of the function is to be added and displayed in the
+case its a global function. This attribute is for ``qdoc`` only.
+
For the *optional* attributes **allow-thread**, **exception-handling**,
**overload-number** and **snake-case**, see :ref:`modify-function`.
using namespace Qt::StringLiterals;
-static const char ENABLE_PYSIDE_EXTENSIONS[] = "enable-pyside-extensions";
-static const char AVOID_PROTECTED_HACK[] = "avoid-protected-hack";
+static constexpr auto ENABLE_PYSIDE_EXTENSIONS = "enable-pyside-extensions"_L1;
+static constexpr auto AVOID_PROTECTED_HACK = "avoid-protected-hack"_L1;
struct GeneratorOptions
{
GeneratorOptions Generator::GeneratorPrivate::m_options;
+// Kept as a variable for a potential Qt-in-namespace support
+QString Generator::m_gsp = "::"_L1;
+
Generator::Generator() : m_d(new GeneratorPrivate)
{
}
QList<OptionDescription> Generator::options()
{
return {
- {QLatin1StringView(AVOID_PROTECTED_HACK),
+ {AVOID_PROTECTED_HACK,
u"Avoid the use of the '#define protected public' hack."_s},
- {QLatin1StringView(ENABLE_PYSIDE_EXTENSIONS),
+ {ENABLE_PYSIDE_EXTENSIONS,
u"Enable PySide extensions, such as support for signal/slots,\n"
"use this if you are creating a binding for a Qt-based library."_s}
};
{
if (source == OptionSource::CommandLineSingleDash)
return false;
- if (key == QLatin1StringView(ENABLE_PYSIDE_EXTENSIONS))
+ if (key == ENABLE_PYSIDE_EXTENSIONS)
return ( m_options->usePySideExtensions = true);
- if (key == QLatin1StringView(AVOID_PROTECTED_HACK))
+ if (key == AVOID_PROTECTED_HACK)
return ( m_options->avoidProtectedHack = true);
return false;
}
const AbstractMetaType innerType = smartPointerType.getSmartPointerInnerType();
smartPointerType.typeEntry()->qualifiedCppName();
QString fileName = smartPointerType.typeEntry()->qualifiedCppName().toLower();
- fileName.replace(u"::"_s, u"_"_s);
- fileName.append(u"_"_s);
+ fileName.append(u'_');
fileName.append(innerType.name().toLower());
-
+ fileName.replace(u"::"_s, u"_"_s); // std::shared_ptr<std::string>
return fileName;
}
QString result = type->qualifiedCppName();
if (type->isArray())
type = std::static_pointer_cast<const ArrayTypeEntry>(type)->nestedTypeEntry();
- if (!isCppPrimitive(type))
- result.prepend(u"::"_s);
- return result;
+ return isCppPrimitive(type) ? result : addGlobalScopePrefix(result);
}
QString Generator::getFullTypeName(const AbstractMetaType &type)
if (type.isVoidPointer())
return u"void*"_s;
if (type.typeEntry()->isContainer())
- return u"::"_s + type.cppSignature();
+ return addGlobalScopePrefix(type.cppSignature());
QString typeName;
if (type.typeEntry()->isComplex() && type.hasInstantiations())
typeName = getFullTypeNameWithoutModifiers(type);
QString Generator::getFullTypeName(const AbstractMetaClassCPtr &metaClass)
{
- return u"::"_s + metaClass->qualifiedCppName();
+ const QString &qualName = metaClass->qualifiedCppName();
+ // Typedefs are generated into the global namespace
+ return metaClass->isTypeDef() ? qualName : addGlobalScopePrefix(qualName);
}
QString Generator::getFullTypeNameWithoutModifiers(const AbstractMetaType &type)
}
while (typeName.endsWith(u'*') || typeName.endsWith(u' '))
typeName.chop(1);
- return u"::"_s + typeName;
+ return addGlobalScopePrefix(typeName);
}
std::optional<DefaultValue>
return packageNameIn;
}
+QString Generator::addGlobalScopePrefix(const QString &t)
+{
+ return t.startsWith("std::"_L1) ? t : m_gsp + t;
+}
+
+QString Generator::globalScopePrefix(const GeneratorContext &classContext)
+{
+ return classContext.useWrapper() ? QString{} : m_gsp;
+}
+
template<typename T>
static QString getClassTargetFullName_(T t, bool includePackageName)
{
*/
virtual QString subDirectoryForPackage(QString packageName = QString()) const;
+ static QString addGlobalScopePrefix(const QString &t);
+ static QString globalScopePrefix(const GeneratorContext &classContext);
+
+ static QString m_gsp;
+
private:
struct GeneratorPrivate;
GeneratorPrivate *m_d;
using namespace Qt::StringLiterals;
+static inline QString classScope(const AbstractMetaClassCPtr &metaClass)
+{
+ return metaClass->fullName();
+}
+
+struct DocPackage
+{
+ QStringList classPages;
+ QStringList decoratorPages;
+ AbstractMetaFunctionCList globalFunctions;
+ AbstractMetaEnumList globalEnums;
+};
+
struct DocGeneratorOptions
{
QtXmlToSphinxParameters parameters;
AbstractMetaFunctionCPtr notify;
};
- AbstractMetaFunctionCList constructors;
- AbstractMetaFunctionCList allFunctions; // Except constructors
+ AbstractMetaFunctionCList allFunctions;
AbstractMetaFunctionCList tocNormalFunctions; // Index lists
AbstractMetaFunctionCList tocVirtuals;
AbstractMetaFunctionCList tocSignalFunctions;
return lhs.name < rhs.name;
}
-static QString propertyRefTarget(const AbstractMetaClassCPtr &cppClass, const QString &name)
+static QString propertyRefTarget(const QString &name)
{
- QString result = cppClass->fullName() + u'.' + name;
- result.replace(u"::"_s, u"."_s);
+ QString result = name;
// For sphinx referencing, disambiguate the target from the getter name
- // by inserting an invisible "Hangul choseong filler" character.
- result.insert(1, QChar(0x115F));
+ // by appending an invisible "Hangul choseong filler" character.
+ result.append(QChar(0x115F));
return result;
}
-static inline QString additionalDocumentationOption() { return QStringLiteral("additional-documentation"); }
+constexpr auto additionalDocumentationOption = "additional-documentation"_L1;
-static inline QString none() { return QStringLiteral("None"); }
+constexpr auto none = "None"_L1;
static bool shouldSkip(const AbstractMetaFunctionCPtr &func)
{
static bool functionSort(const AbstractMetaFunctionCPtr &func1, const AbstractMetaFunctionCPtr &func2)
{
- return func1->name() < func2->name();
+ const bool ctor1 = func1->isConstructor();
+ if (ctor1 != func2->isConstructor())
+ return ctor1;
+ const QString &name1 = func1->name();
+ const QString &name2 = func2->name();
+ if (name1 != name2)
+ return name1 < name2;
+ return func1->arguments().size() < func2->arguments().size();
}
static inline QVersionNumber versionOf(const TypeEntryCPtr &te)
return {};
}
-// Format a documentation reference (meth/attr): ":meth:`name<target>`"
-// We do not use the short form ":meth:`~target`" since that adds parentheses ()
-// for functions where we list the parameters instead.
struct docRef
{
- explicit docRef(const char *kind, const QString &name,
- const AbstractMetaClassCPtr &cppClass) :
- m_kind(kind), m_name(name), m_cppClass(cppClass) {}
+ explicit docRef(const char *kind, QAnyStringView name) :
+ m_kind(kind), m_name(name) {}
const char *m_kind;
- const QString &m_name;
- const AbstractMetaClassCPtr m_cppClass;
+ QAnyStringView m_name;
};
static TextStream &operator<<(TextStream &s, const docRef &dr)
{
- QString className = dr.m_cppClass->fullName();
- className.replace(u"::"_s, u"."_s);
- s << ':' << dr.m_kind << ":`" << dr.m_name << '<';
- if (!dr.m_name.startsWith(className))
- s << className << '.';
- s << dr.m_name << ">`";
+ s << ':' << dr.m_kind << ":`" << dr.m_name << '`';
return s;
}
+static QString fileNameToTocEntry(const QString &fileName)
+{
+ constexpr auto rstSuffix = ".rst"_L1;
+
+ QString result = fileName;
+ if (result.endsWith(rstSuffix))
+ result.chop(rstSuffix.size()); // Remove the .rst extension
+ // skip namespace if necessary
+ auto lastDot = result.lastIndexOf(u'.');
+ if (lastDot != -1)
+ result.remove(0, lastDot + 1);
+ return result;
+}
+
+static void readExtraDoc(const QFileInfo &fi,
+ const QString &moduleName,
+ const QString &outputDir,
+ DocPackage *docPackage, QStringList *extraTocEntries)
+{
+ // Strip to "Property.rst" in output directory
+ const QString newFileName = fi.fileName().mid(moduleName.size() + 1);
+ QFile sourceFile(fi.absoluteFilePath());
+ if (!sourceFile.open(QIODevice::ReadOnly|QIODevice::Text)) {
+ qCWarning(lcShibokenDoc, "%s", qPrintable(msgCannotOpenForReading(sourceFile)));
+ return;
+ }
+ const QByteArray contents = sourceFile.readAll();
+ sourceFile.close();
+ QFile targetFile(outputDir + u'/' + newFileName);
+ if (!targetFile.open(QIODevice::WriteOnly|QIODevice::Text)) {
+ qCWarning(lcShibokenDoc, "%s", qPrintable(msgCannotOpenForWriting(targetFile)));
+ return;
+ }
+ targetFile.write(contents);
+ if (contents.contains("decorator::"))
+ docPackage->decoratorPages.append(newFileName);
+ else
+ docPackage->classPages.append(newFileName);
+ extraTocEntries->append(fileNameToTocEntry(newFileName));
+}
+
// Format a short documentation reference (automatically dropping the prefix
// by using '~'), usable for property/attributes ("attr").
struct shortDocRef
{
- explicit shortDocRef(const char *kind, const QString &target) :
- m_kind(kind), m_target(target) {}
+ explicit shortDocRef(const char *kind, QAnyStringView name) :
+ m_kind(kind), m_name(name) {}
const char *m_kind;
- const QString &m_target;
+ QAnyStringView m_name;
};
static TextStream &operator<<(TextStream &s, const shortDocRef &sdr)
{
- s << ':' << sdr.m_kind << ":`~" << sdr.m_target << '`';
+ s << ':' << sdr.m_kind << ":`~" << sdr.m_name << '`';
return s;
}
struct functionRef : public docRef
{
- explicit functionRef(const QString &name, const AbstractMetaClassCPtr &cppClass) :
- docRef("meth", name, cppClass) {}
+ explicit functionRef(QAnyStringView name) : docRef("meth", name) {}
+};
+
+struct classRef : public shortDocRef
+{
+ explicit classRef(QAnyStringView name) : shortDocRef("class", name) {}
+};
+
+struct propRef : public shortDocRef // Attribute/property (short) reference
+{
+ explicit propRef(const QString &target) :
+ shortDocRef("attr", target) {}
};
-struct functionTocEntry // Format a TOC entry for a function
+struct headline
{
- explicit functionTocEntry(const AbstractMetaFunctionCPtr& func,
- const AbstractMetaClassCPtr &cppClass) :
- m_func(func), m_cppClass(cppClass) {}
+ explicit headline(QAnyStringView title, char underLineChar = '-') :
+ m_title(title), m_underLineChar(underLineChar) {}
- AbstractMetaFunctionCPtr m_func;
- const AbstractMetaClassCPtr m_cppClass;
+ QAnyStringView m_title;
+ char m_underLineChar;
};
-static TextStream &operator<<(TextStream &s, const functionTocEntry &ft)
+static TextStream &operator<<(TextStream &s, const headline &h)
{
- s << functionRef(QtDocGenerator::getFuncName(ft.m_func), ft.m_cppClass)
- << ' ' << QtDocGenerator::formatArgs(ft.m_func);
+ s << h.m_title << '\n' << Pad(h.m_underLineChar, h.m_title.size()) << "\n\n";
return s;
}
-struct propRef : public shortDocRef // Attribute/property (short) reference
+struct pyClass
{
- explicit propRef(const QString &target) :
- shortDocRef("attr", target) {}
+ explicit pyClass(QAnyStringView name) : m_name(name) {}
+
+ QAnyStringView m_name;
+};
+
+static TextStream &operator<<(TextStream &s, pyClass c)
+{
+ s << ".. py:class:: " << c.m_name << "\n\n";
+ return s;
+}
+
+struct currentModule
+{
+ explicit currentModule(QAnyStringView module) : m_module(module) {}
+
+ QAnyStringView m_module;
};
+static TextStream &operator<<(TextStream &s, const currentModule &m)
+{
+ s << ".. currentmodule:: " << m.m_module << "\n\n\n";
+ return s;
+}
+
DocGeneratorOptions QtDocGenerator::m_options;
QtDocGenerator::QtDocGenerator()
}
void QtDocGenerator::writeFormattedBriefText(TextStream &s, const Documentation &doc,
- const AbstractMetaClassCPtr &metaclass) const
+ const QString &scope) const
{
- writeFormattedText(s, doc.brief(), doc.format(), metaclass);
+ writeFormattedText(s, doc.brief(), doc.format(), scope);
}
void QtDocGenerator::writeFormattedDetailedText(TextStream &s, const Documentation &doc,
- const AbstractMetaClassCPtr &metaclass) const
+ const QString &scope) const
{
- writeFormattedText(s, doc.detailed(), doc.format(), metaclass);
+ writeFormattedText(s, doc.detailed(), doc.format(), scope);
}
void QtDocGenerator::writeFormattedText(TextStream &s, const QString &doc,
Documentation::Format format,
- const AbstractMetaClassCPtr &metaClass) const
+ const QString &scope) const
{
- QString metaClassName;
-
- if (metaClass)
- metaClassName = metaClass->fullName();
-
if (format == Documentation::Native) {
- QtXmlToSphinx x(this, m_options.parameters, doc, metaClassName);
+ QtXmlToSphinx x(this, m_options.parameters, doc, scope);
s << x;
} else {
const auto lines = QStringView{doc}.split(u'\n');
for (qsizetype i = 0, size = classes.size(); i < size; ++i) {
if (i > 0)
s << ", ";
- s << ":ref:`" << classes.at(i)->name() << '`';
+ s << classRef(classes.at(i)->fullName());
}
s << "\n\n";
}
AbstractMetaClassCPtr metaClass = classContext.metaClass();
qCDebug(lcShibokenDoc).noquote().nospace() << "Generating Documentation for " << metaClass->fullName();
- m_packages[metaClass->package()] << fileNameForContext(classContext);
+ m_packages[metaClass->package()].classPages << fileNameForContext(classContext);
m_docParser->setPackageName(metaClass->package());
m_docParser->fillDocumentation(std::const_pointer_cast<AbstractMetaClass>(metaClass));
- QString className = metaClass->name();
- s << ".. _" << className << ":" << "\n\n";
- s << ".. currentmodule:: " << metaClass->package() << "\n\n\n";
-
- s << className << '\n';
- s << Pad('*', className.size()) << "\n\n";
+ s << currentModule(metaClass->package()) << pyClass(metaClass->name());
+ Indentation indent(s);
auto documentation = metaClass->documentation();
+ const QString scope = classScope(metaClass);
if (documentation.hasBrief())
- writeFormattedBriefText(s, documentation, metaClass);
+ writeFormattedBriefText(s, documentation, scope);
if (!metaClass->baseClasses().isEmpty()) {
if (m_options.inheritanceDiagram) {
const GeneratorDocumentation doc = generatorDocumentation(metaClass);
if (!doc.allFunctions.isEmpty() || !doc.properties.isEmpty()) {
- s << "\nSynopsis\n--------\n\n";
- writePropertyToc(s, doc, metaClass);
- writeFunctionToc(s, u"Functions"_s, metaClass, doc.tocNormalFunctions);
- writeFunctionToc(s, u"Virtual functions"_s, metaClass, doc.tocVirtuals);
- writeFunctionToc(s, u"Slots"_s, metaClass, doc.tocSlotFunctions);
- writeFunctionToc(s, u"Signals"_s, metaClass, doc.tocSignalFunctions);
- writeFunctionToc(s, u"Static functions"_s, metaClass, doc.tocStaticFunctions);
+ s << '\n' << headline("Synopsis");
+ writePropertyToc(s, doc);
+ writeFunctionToc(s, u"Methods"_s, doc.tocNormalFunctions);
+ writeFunctionToc(s, u"Virtual methods"_s, doc.tocVirtuals);
+ writeFunctionToc(s, u"Slots"_s, doc.tocSlotFunctions);
+ writeFunctionToc(s, u"Signals"_s, doc.tocSignalFunctions);
+ writeFunctionToc(s, u"Static functions"_s, doc.tocStaticFunctions);
}
s << "\n.. note::\n"
" translation, you can also let us know by creating a ticket on\n"
" https:/bugreports.qt.io/projects/PYSIDE\n\n";
- s << "\nDetailed Description\n"
- "--------------------\n\n"
- << ".. _More:\n";
+ s << '\n' << headline("Detailed Description") << ".. _More:\n";
- writeInjectDocumentation(s, TypeSystem::DocModificationPrepend, metaClass, nullptr);
- if (!writeInjectDocumentation(s, TypeSystem::DocModificationReplace, metaClass, nullptr))
- writeFormattedDetailedText(s, documentation, metaClass);
+ writeInjectDocumentation(s, TypeSystem::DocModificationPrepend, metaClass);
+ if (!writeInjectDocumentation(s, TypeSystem::DocModificationReplace, metaClass))
+ writeFormattedDetailedText(s, documentation, scope);
+ writeInjectDocumentation(s, TypeSystem::DocModificationAppend, metaClass);
- if (!metaClass->isNamespace())
- writeConstructors(s, metaClass, doc.constructors);
+ writeEnums(s, metaClass->enums(), scope);
if (!doc.properties.isEmpty())
writeProperties(s, doc, metaClass);
- writeEnums(s, metaClass);
if (!metaClass->isNamespace())
writeFields(s, metaClass);
- QString lastName;
- for (const auto &func : std::as_const(doc.allFunctions)) {
- const bool indexed = func->name() != lastName;
- lastName = func->name();
- s << (func->isStatic() ? ".. py:staticmethod:: " : ".. py:method:: ");
- writeFunction(s, metaClass, func, indexed);
- }
-
- writeInjectDocumentation(s, TypeSystem::DocModificationAppend, metaClass, nullptr);
+ writeFunctions(s, doc.allFunctions, metaClass, scope);
}
void QtDocGenerator::writeFunctionToc(TextStream &s, const QString &title,
- const AbstractMetaClassCPtr &cppClass,
const AbstractMetaFunctionCList &functions)
{
if (!functions.isEmpty()) {
- s << title << '\n'
- << Pad('^', title.size()) << '\n';
-
- s << ".. container:: function_list\n\n" << indent;
- for (const auto &func : functions)
- s << "* def " << functionTocEntry(func, cppClass) << '\n';
+ s << headline(title, '^')
+ << ".. container:: function_list\n\n" << indent;
+ // Functions are sorted by the Metabuilder; erase overloads
+ QStringList toc;
+ toc.reserve(functions.size());
+ std::transform(functions.cbegin(), functions.end(),
+ std::back_inserter(toc), getFuncName);
+ toc.erase(std::unique(toc.begin(), toc.end()), toc.end());
+ for (const auto &func : toc)
+ s << "* def " << functionRef(func) << '\n';
s << outdent << "\n\n";
}
}
void QtDocGenerator::writePropertyToc(TextStream &s,
- const GeneratorDocumentation &doc,
- const AbstractMetaClassCPtr &cppClass)
+ const GeneratorDocumentation &doc)
{
if (doc.properties.isEmpty())
return;
- const QString title = u"Properties"_s;
- s << title << '\n'
- << Pad('^', title.size()) << '\n';
-
- s << ".. container:: function_list\n\n" << indent;
+ s << headline("Properties", '^')
+ << ".. container:: function_list\n\n" << indent;
for (const auto &prop : doc.properties) {
- s << "* " << propRef(propertyRefTarget(cppClass, prop.name));
+ s << "* " << propRef(propertyRefTarget(prop.name));
if (prop.documentation.hasBrief())
s << " - " << prop.documentation.brief();
s << '\n';
<< "``from __feature__ import true_property`` is used or via accessor "
<< "functions otherwise.\n\n";
+ const QString scope = classScope(cppClass);
for (const auto &prop : doc.properties) {
const QString type = translateToPythonType(prop.type, cppClass, /* createRef */ false);
- s << ".. py:property:: " << propertyRefTarget(cppClass, prop.name)
+ s << ".. py:property:: " << propertyRefTarget(prop.name)
<< "\n :type: " << type << "\n\n\n";
if (!prop.documentation.isEmpty())
- writeFormattedText(s, prop.documentation.detailed(), Documentation::Native, cppClass);
+ writeFormattedText(s, prop.documentation.detailed(), Documentation::Native, scope);
s << "**Access functions:**\n";
if (prop.getter)
- s << " * " << functionTocEntry(prop.getter, cppClass) << '\n';
+ s << " * " << functionRef(prop.getter->name()) << '\n';
if (prop.setter)
- s << " * " << functionTocEntry(prop.setter, cppClass) << '\n';
+ s << " * " << functionRef(prop.setter->name()) << '\n';
if (prop.reset)
- s << " * " << functionTocEntry(prop.reset, cppClass) << '\n';
+ s << " * " << functionRef(prop.reset->name()) << '\n';
if (prop.notify)
- s << " * Signal " << functionTocEntry(prop.notify, cppClass) << '\n';
+ s << " * Signal " << functionRef(prop.notify->name()) << '\n';
s << '\n';
}
}
-void QtDocGenerator::writeEnums(TextStream &s, const AbstractMetaClassCPtr &cppClass) const
+void QtDocGenerator::writeEnums(TextStream &s, const AbstractMetaEnumList &enums,
+ const QString &scope) const
{
- static const QString section_title = u".. attribute:: "_s;
-
- for (const AbstractMetaEnum &en : cppClass->enums()) {
- s << section_title << cppClass->fullName() << '.' << en.name() << "\n\n";
- writeFormattedDetailedText(s, en.documentation(), cppClass);
+ for (const AbstractMetaEnum &en : enums) {
+ s << pyClass(en.name());
+ Indentation indent(s);
+ writeFormattedDetailedText(s, en.documentation(), scope);
const auto version = versionOf(en.typeEntry());
if (!version.isNull())
s << rstVersionAdded(version);
void QtDocGenerator::writeFields(TextStream &s, const AbstractMetaClassCPtr &cppClass) const
{
- static const QString section_title = u".. attribute:: "_s;
+ constexpr auto section_title = ".. attribute:: "_L1;
+ const QString scope = classScope(cppClass);
for (const AbstractMetaField &field : cppClass->fields()) {
s << section_title << cppClass->fullName() << "." << field.name() << "\n\n";
- writeFormattedDetailedText(s, field.documentation(), cppClass);
+ writeFormattedDetailedText(s, field.documentation(), scope);
}
}
-void QtDocGenerator::writeConstructors(TextStream &s, const AbstractMetaClassCPtr &cppClass,
- const AbstractMetaFunctionCList &constructors) const
-{
- static const QString sectionTitle = u".. class:: "_s;
-
- bool first = true;
- QHash<QString, AbstractMetaArgument> arg_map;
-
- if (constructors.isEmpty()) {
- s << sectionTitle << cppClass->fullName();
- } else {
- QByteArray pad;
- for (const auto &func : constructors) {
- s << pad;
- if (first) {
- first = false;
- s << sectionTitle;
- pad = QByteArray(sectionTitle.size(), ' ');
- }
- s << functionSignature(cppClass, func) << "\n\n";
-
- const auto version = versionOf(func->typeEntry());
- if (!version.isNull())
- s << pad << rstVersionAdded(version);
- if (func->isDeprecated())
- s << pad << rstDeprecationNote("constructor");
-
- const AbstractMetaArgumentList &arguments = func->arguments();
- for (const AbstractMetaArgument &arg : arguments) {
- if (!arg_map.contains(arg.name())) {
- arg_map.insert(arg.name(), arg);
- }
- }
- }
- }
-
- s << '\n';
-
- for (auto it = arg_map.cbegin(), end = arg_map.cend(); it != end; ++it) {
- s.indent(2);
- writeParameterType(s, cppClass, it.value());
- s.outdent(2);
- }
-
- s << '\n';
-
- for (const auto &func : constructors)
- writeFormattedDetailedText(s, func->documentation(), cppClass);
-}
-
QString QtDocGenerator::formatArgs(const AbstractMetaFunctionCPtr &func)
{
QString ret = u"("_s;
|| defValue.startsWith(u"QList")) {
defValue = u"list()"_s;
} else if (defValue == u"QVariant()") {
- defValue = none();
+ defValue = none;
} else {
defValue.replace(u"::"_s, u"."_s);
if (defValue == u"nullptr")
- defValue = none();
+ defValue = none;
else if (defValue == u"0" && arg.type().isObject())
- defValue = none();
+ defValue = none;
}
ret += u'=' + defValue;
}
}
}
-bool QtDocGenerator::writeInjectDocumentation(TextStream &s,
- TypeSystem::DocModificationMode mode,
- const AbstractMetaClassCPtr &cppClass,
- const AbstractMetaFunctionCPtr &func)
+bool QtDocGenerator::writeDocModifications(TextStream &s,
+ const DocModificationList &mods,
+ TypeSystem::DocModificationMode mode,
+ const QString &scope) const
{
- Indentation indentation(s);
bool didSomething = false;
-
- const DocModificationList mods = DocParser::getDocModifications(cppClass, func);
-
for (const DocModification &mod : mods) {
if (mod.mode() == mode) {
switch (mod.format()) {
case TypeSystem::NativeCode:
- writeFormattedText(s, mod.code(), Documentation::Native, cppClass);
+ writeFormattedText(s, mod.code(), Documentation::Native, scope);
didSomething = true;
break;
case TypeSystem::TargetLangCode:
- writeFormattedText(s, mod.code(), Documentation::Target, cppClass);
+ writeFormattedText(s, mod.code(), Documentation::Target, scope);
didSomething = true;
break;
default:
}
}
}
+ return didSomething;
+}
+bool QtDocGenerator::writeInjectDocumentation(TextStream &s,
+ TypeSystem::DocModificationMode mode,
+ const AbstractMetaClassCPtr &cppClass) const
+{
+ const bool didSomething =
+ writeDocModifications(s, DocParser::getDocModifications(cppClass),
+ mode, classScope(cppClass));
s << '\n';
// FIXME PYSIDE-7: Deprecate the use of doc string on glue code.
// This is pre "add-function" and "inject-documentation" tags.
const TypeSystem::CodeSnipPosition pos = mode == TypeSystem::DocModificationPrepend
? TypeSystem::CodeSnipPositionBeginning : TypeSystem::CodeSnipPositionEnd;
- if (func)
- writeDocSnips(s, func->injectedCodeSnips(), pos, TypeSystem::TargetLangCode);
- else
- writeDocSnips(s, cppClass->typeEntry()->codeSnips(), pos, TypeSystem::TargetLangCode);
+ writeDocSnips(s, cppClass->typeEntry()->codeSnips(), pos, TypeSystem::TargetLangCode);
return didSomething;
}
-QString QtDocGenerator::functionSignature(const AbstractMetaClassCPtr &cppClass,
- const AbstractMetaFunctionCPtr &func)
+bool QtDocGenerator::writeInjectDocumentation(TextStream &s,
+ TypeSystem::DocModificationMode mode,
+ const DocModificationList &modifications,
+ const AbstractMetaFunctionCPtr &func,
+ const QString &scope) const
{
- QString funcName = cppClass->fullName();
- if (!func->isConstructor())
- funcName += u'.' + getFuncName(func);
+ const bool didSomething = writeDocModifications(s, modifications, mode, scope);
+ s << '\n';
- return funcName + formatArgs(func);
+ // FIXME PYSIDE-7: Deprecate the use of doc string on glue code.
+ // This is pre "add-function" and "inject-documentation" tags.
+ const TypeSystem::CodeSnipPosition pos = mode == TypeSystem::DocModificationPrepend
+ ? TypeSystem::CodeSnipPositionBeginning : TypeSystem::CodeSnipPositionEnd;
+ writeDocSnips(s, func->injectedCodeSnips(), pos, TypeSystem::TargetLangCode);
+ return didSomething;
}
static QString inline toRef(const QString &t)
{
- return ":any:`"_L1 + t + u'`';
+ return ":class:`~"_L1 + t + u'`';
}
QString QtDocGenerator::translateToPythonType(const AbstractMetaType &type,
bool createRef) const
{
static const QStringList nativeTypes =
- {boolT(), floatT(), intT(), pyObjectT(), pyStrT()};
+ {boolT, floatT, intT, pyObjectT, pyStrT};
QString name = type.name();
if (nativeTypes.contains(name))
const auto &basicName = basicReferencedTypeEntry(type.typeEntry())->name();
if (AbstractMetaType::cppSignedIntTypes().contains(basicName)
|| AbstractMetaType::cppUnsignedIntTypes().contains(basicName)) {
- return intT();
+ return intT;
}
if (AbstractMetaType::cppFloatTypes().contains(basicName))
- return floatT();
+ return floatT;
}
static const QSet<QString> stringTypes = {
u"uchar"_s, u"std::string"_s, u"std::wstring"_s,
u"std::stringview"_s, u"std::wstringview"_s,
- qStringT(), u"QStringView"_s, u"QAnyStringView"_s, u"QUtf8StringView"_s
+ qStringT, u"QStringView"_s, u"QAnyStringView"_s, u"QUtf8StringView"_s
};
if (stringTypes.contains(name))
- return pyStrT();
+ return pyStrT;
static const QHash<QString, QString> typeMap = {
- { cPyObjectT(), pyObjectT() },
+ { cPyObjectT, pyObjectT },
{ u"QStringList"_s, u"list of strings"_s },
- { qVariantT(), pyObjectT() }
+ { qVariantT, pyObjectT }
};
const auto found = typeMap.constFind(name);
if (found != typeMap.cend())
if (type.isFlags()) {
const auto fte = std::static_pointer_cast<const FlagsTypeEntry>(type.typeEntry());
- auto enumName = fte->originator()->targetLangName();
+ auto enumTypeEntry = fte->originator();
+ auto enumName = enumTypeEntry->targetLangName();
+ if (createRef)
+ enumName.prepend(enumTypeEntry->targetLangPackage() + u'.');
return "Combination of "_L1 + (createRef ? toRef(enumName) : enumName);
+ } else if (type.isEnum()) {
+ auto enumTypeEntry = std::static_pointer_cast<const EnumTypeEntry>(type.typeEntry());
+ auto enumName = enumTypeEntry->targetLangName();
+ if (createRef)
+ enumName.prepend(enumTypeEntry->targetLangPackage() + u'.');
+ return createRef ? toRef(enumName) : enumName;
}
if (type.isConstant() && name == "char"_L1 && type.indirections() == 1)
}
if (auto k = AbstractMetaClass::findClass(api().classes(), type.typeEntry()))
- return createRef ? toRef(k->fullName()) : k->fullName();
+ return createRef ? toRef(k->fullName()) : k->name();
return createRef ? toRef(name) : name;
}
QString QtDocGenerator::getFuncName(const AbstractMetaFunctionCPtr &cppFunc)
{
+ if (cppFunc->isConstructor())
+ return "__init__"_L1;
QString result = cppFunc->name();
if (cppFunc->isOperatorOverload()) {
const QString pythonOperator = Generator::pythonOperatorFunctionName(result);
s << '\n';
}
-void QtDocGenerator::writeFunction(TextStream &s, const AbstractMetaClassCPtr &cppClass,
- const AbstractMetaFunctionCPtr &func, bool indexed)
+static bool containsFunctionDirective(const DocModification &dm)
{
- s << functionSignature(cppClass, func);
+ return dm.mode() != TypeSystem::DocModificationXPathReplace
+ && dm.code().contains(".. py:"_L1);
+}
- {
+void QtDocGenerator::writeFunctions(TextStream &s, const AbstractMetaFunctionCList &funcs,
+ const AbstractMetaClassCPtr &cppClass, const QString &scope)
+{
+ QString lastName;
+ for (const auto &func : funcs) {
+ const bool indexed = func->name() != lastName;
+ lastName = func->name();
+ writeFunction(s, func, cppClass, scope, indexed);
+ }
+}
+
+void QtDocGenerator::writeFunction(TextStream &s, const AbstractMetaFunctionCPtr &func,
+ const AbstractMetaClassCPtr &cppClass,
+ const QString &scope, bool indexed)
+{
+ const auto modifications = DocParser::getDocModifications(func, cppClass);
+
+ // Enable injecting parameter documentation by adding a complete function directive.
+ if (std::none_of(modifications.cbegin(), modifications.cend(), containsFunctionDirective)) {
+ if (func->ownerClass() == nullptr)
+ s << ".. py:function:: ";
+ else
+ s << (func->isStatic() ? ".. py:staticmethod:: " : ".. py:method:: ");
+ s << getFuncName(func) << formatArgs(func);
Indentation indentation(s);
if (!indexed)
s << "\n:noindex:";
- if (func->attributes().testFlag(AbstractMetaFunction::Attribute::FinalCppMethod))
+ if (func->cppAttributes().testFlag(FunctionAttribute::Final))
s << "\n:final:";
else if (func->isAbstract())
s << "\n:abstractmethod:";
if (func->isDeprecated())
s << rstDeprecationNote("function");
}
- writeInjectDocumentation(s, TypeSystem::DocModificationPrepend, cppClass, func);
- if (!writeInjectDocumentation(s, TypeSystem::DocModificationReplace, cppClass, func)) {
- writeFormattedBriefText(s, func->documentation(), cppClass);
- writeFormattedDetailedText(s, func->documentation(), cppClass);
- }
- writeInjectDocumentation(s, TypeSystem::DocModificationAppend, cppClass, func);
+
+ writeFunctionDocumentation(s, func, modifications, scope);
if (auto propIndex = func->propertySpecIndex(); propIndex >= 0) {
const QString name = cppClass->propertySpecs().at(propIndex).name();
- const QString target = propertyRefTarget(cppClass, name);
+ const QString target = propertyRefTarget(name);
if (func->isPropertyReader())
s << "\nGetter of property " << propRef(target) << " .\n\n";
else if (func->isPropertyWriter())
}
}
-static void writeFancyToc(TextStream& s, const QStringList& items)
+void QtDocGenerator::writeFunctionDocumentation(TextStream &s, const AbstractMetaFunctionCPtr &func,
+ const DocModificationList &modifications,
+ const QString &scope) const
+
+{
+ writeInjectDocumentation(s, TypeSystem::DocModificationPrepend, modifications, func, scope);
+ if (!writeInjectDocumentation(s, TypeSystem::DocModificationReplace, modifications, func, scope)) {
+ writeFormattedBriefText(s, func->documentation(), scope);
+ writeFormattedDetailedText(s, func->documentation(), scope);
+ }
+ writeInjectDocumentation(s, TypeSystem::DocModificationAppend, modifications, func, scope);
+}
+
+static QStringList fileListToToc(const QStringList &items)
+{
+ QStringList result;
+ result.reserve(items.size());
+ std::transform(items.cbegin(), items.cend(), std::back_inserter(result),
+ fileNameToTocEntry);
+ return result;
+}
+
+static QStringList functionListToToc(const AbstractMetaFunctionCList &functions)
+{
+ QStringList result;
+ result.reserve(functions.size());
+ for (const auto &f : functions)
+ result.append(f->name());
+ // Functions are sorted by the Metabuilder; erase overloads
+ result.erase(std::unique(result.begin(), result.end()), result.end());
+ return result;
+}
+
+static QStringList enumListToToc(const AbstractMetaEnumList &enums)
+{
+ QStringList result;
+ result.reserve(enums.size());
+ for (const auto &e : enums)
+ result.append(e.name());
+ return result;
+}
+
+// Sort entries for a TOC by first character, dropping the
+// leading common Qt prefixes like 'Q'.
+static QChar sortKey(const QString &key)
+{
+ const auto size = key.size();
+ if (size >= 2 && (key.at(0) == u'Q' || key.at(0) == u'q')
+ && (key.at(1).isUpper() || key.at(1).isDigit())) {
+ return key.at(1); // "QClass" -> 'C', "qSin()" -> 'S', 'Q3DSurfaceWidget' -> '3'
+ }
+ if (size >= 3 && key.startsWith("Q_"_L1))
+ return key.at(2).toUpper(); // "Q_ARG" -> 'A'
+ if (size >= 4 && key.startsWith("QT_"_L1))
+ return key.at(3).toUpper(); // "QT_TR" -> 'T'
+ auto idx = 0;
+ for (; idx < size && key.at(idx) == u'_'; ++idx) {
+ } // "__init__" -> 'I'
+ return idx < size ? key.at(idx).toUpper() : u'A';
+}
+
+static void writeFancyToc(TextStream& s, QAnyStringView title,
+ const QStringList& items,
+ QLatin1StringView referenceType)
{
using TocMap = QMap<QChar, QStringList>;
+
+ if (items.isEmpty())
+ return;
+
TocMap tocMap;
- QChar idx;
- for (QString item : items) {
- if (item.isEmpty())
- continue;
- item.chop(4); // Remove the .rst extension
- // skip namespace if necessary
- const QString className = item.split(u'.').last();
- if (className.startsWith(u'Q') && className.length() > 1)
- idx = className[1];
- else
- idx = className[0];
- tocMap[idx] << item;
- }
+ for (const QString &item : items)
+ tocMap[sortKey(item)] << item;
static const qsizetype numColumns = 4;
row.clear();
row << QtXmlToSphinx::TableCell(QString{});
}
- const QString entry = u"* :doc:`"_s + item + u'`';
+ const QString entry = "* :"_L1 + referenceType + ":`"_L1 + item + u'`';
row << QtXmlToSphinx::TableCell(entry);
}
if (row.size() > 1)
}
table.normalize();
- s << ".. container:: pysidetoc\n\n";
+ s << '\n' << headline(title) << ".. container:: pysidetoc\n\n";
table.format(s);
}
bool QtDocGenerator::finishGeneration()
{
- if (!api().classes().isEmpty())
+ for (const auto &f : api().globalFunctions()) {
+ auto ncf = std::const_pointer_cast<AbstractMetaFunction>(f);
+ m_docParser->fillGlobalFunctionDocumentation(ncf);
+ m_packages[f->targetLangPackage()].globalFunctions.append(f);
+ }
+
+ for (auto e : api().globalEnums()) {
+ m_docParser->fillGlobalEnumDocumentation(e);
+ m_packages[e.typeEntry()->targetLangPackage()].globalEnums.append(e);
+ }
+
+ if (!m_packages.isEmpty())
writeModuleDocumentation();
if (!m_options.additionalDocumentationList.isEmpty())
writeAdditionalDocumentation();
return true;
}
+// Remove function entries that have extra documentation pages
+static inline void removeExtraDocs(const QStringList &extraTocEntries,
+ AbstractMetaFunctionCList *functions)
+{
+ auto predicate = [&extraTocEntries](const AbstractMetaFunctionCPtr &f) {
+ return extraTocEntries.contains(f->name());
+ };
+ functions->erase(std::remove_if(functions->begin(),functions->end(), predicate),
+ functions->end());
+}
+
void QtDocGenerator::writeModuleDocumentation()
{
- QMap<QString, QStringList>::iterator it = m_packages.begin();
- for (; it != m_packages.end(); ++it) {
- std::sort(it.value().begin(), it.value().end());
+ for (auto it = m_packages.begin(), end = m_packages.end(); it != end; ++it) {
+ auto &docPackage = it.value();
+ std::sort(docPackage.classPages.begin(), docPackage.classPages.end());
QString key = it.key();
key.replace(u'.', u'/');
TextStream& s = output.stream;
const QString &title = it.key();
- s << ".. module:: " << title << "\n\n"
- << title << '\n'
- << Pad('*', title.length()) << "\n\n";
+ s << ".. module:: " << title << "\n\n" << headline(title, '*');
// Store the it.key() in a QString so that it can be stripped off unwanted
// information when neeeded. For example, the RST files in the extras directory
moduleName.remove(0, lastIndex + 1);
// Search for extra-sections
+ QStringList extraTocEntries;
if (!m_options.extraSectionDir.isEmpty()) {
QDir extraSectionDir(m_options.extraSectionDir);
if (!extraSectionDir.exists()) {
const QString filter = moduleName + u".?*.rst"_s;
const auto fileList =
extraSectionDir.entryInfoList({filter}, QDir::Files, QDir::Name);
- for (const auto &fi : fileList) {
- // Strip to "Property.rst" in output directory
- const QString newFileName = fi.fileName().mid(moduleName.size() + 1);
- it.value().append(newFileName);
- const QString newFilePath = outputDir + u'/' + newFileName;
- if (QFile::exists(newFilePath))
- QFile::remove(newFilePath);
- if (!QFile::copy(fi.absoluteFilePath(), newFilePath)) {
- qCDebug(lcShibokenDoc).noquote().nospace() << "Error copying extra doc "
- << QDir::toNativeSeparators(fi.absoluteFilePath())
- << " to " << QDir::toNativeSeparators(newFilePath);
- }
- }
+ for (const auto &fi : fileList)
+ readExtraDoc(fi, moduleName, outputDir, &docPackage, &extraTocEntries);
}
+ removeExtraDocs(extraTocEntries, &docPackage.globalFunctions);
+ const bool hasGlobals = !docPackage.globalFunctions.isEmpty()
+ || !docPackage.globalEnums.isEmpty();
+ const QString globalsPage = moduleName + "_globals.rst"_L1;
+
s << ".. container:: hide\n\n" << indent
<< ".. toctree::\n" << indent
<< ":maxdepth: 1\n\n";
- for (const QString &className : std::as_const(it.value()))
+ if (hasGlobals)
+ s << globalsPage << '\n';
+ for (const QString &className : std::as_const(docPackage.classPages))
s << className << '\n';
- s << "\n\n" << outdent << outdent
- << "Detailed Description\n--------------------\n\n";
+ s << "\n\n" << outdent << outdent << headline("Detailed Description");
// module doc is always wrong and C++istic, so go straight to the extra directory!
QFile moduleDoc(m_options.extraSectionDir + u'/' + moduleName
}
}
- s << "\nList of Classes\n"
- << "---------------\n\n";
- writeFancyToc(s, it.value());
+ writeFancyToc(s, "List of Classes", fileListToToc(docPackage.classPages),
+ "class"_L1);
+ writeFancyToc(s, "List of Decorators", fileListToToc(docPackage.decoratorPages),
+ "deco"_L1);
+ writeFancyToc(s, "List of Functions", functionListToToc(docPackage.globalFunctions),
+ "py:func"_L1);
+ writeFancyToc(s, "List of Enumerations", enumListToToc(docPackage.globalEnums),
+ "any"_L1);
output.done();
+
+ if (hasGlobals)
+ writeGlobals(it.key(), outputDir + u'/' + globalsPage, docPackage);
}
}
+void QtDocGenerator::writeGlobals(const QString &package,
+ const QString &fileName,
+ const DocPackage &docPackage)
+{
+ FileOut output(fileName);
+ TextStream &s = output.stream;
+
+ // Write out functions with injected documentation
+ if (!docPackage.globalFunctions.isEmpty()) {
+ s << currentModule(package) << headline("Functions");
+ writeFunctions(s, docPackage.globalFunctions, {}, {});
+ }
+
+ if (!docPackage.globalEnums.isEmpty()) {
+ s << headline("Enumerations");
+ writeEnums(s, docPackage.globalEnums, package);
+ }
+
+ output.done();
+}
+
static inline QString msgNonExistentAdditionalDocFile(const QString &dir,
const QString &fileName)
{
targetDir = outDir.absolutePath();
} else {
if (!outDir.exists(dir) && !outDir.mkdir(dir)) {
- const QString m = QStringLiteral("Cannot create directory ")
- + dir + QStringLiteral(" under ")
+ const QString m = "Cannot create directory "_L1
+ + dir + " under "_L1
+ QDir::toNativeSeparators(outputDirectory());
throw Exception(m);
}
u"Directory used to search for extra documentation sections"_s},
{u"library-source-dir=<dir>"_s,
u"Directory where library source code is located"_s},
- {additionalDocumentationOption() + u"=<file>"_s,
+ {additionalDocumentationOption + u"=<file>"_s,
u"List of additional XML files to be converted to .rst files\n"
"(for example, tutorials)."_s},
{u"inheritance-file=<file>"_s,
m_options->doxygen = true;
return true;
}
- if (key == additionalDocumentationOption()) {
+ if (key == additionalDocumentationOption) {
m_options->additionalDocumentationList = value;
return true;
}
GeneratorDocumentation result;
const auto allFunctions = cppClass->functions();
result.allFunctions.reserve(allFunctions.size());
- for (const auto &func : allFunctions) {
- if (!shouldSkip(func)) {
- if (func->isConstructor())
- result.constructors.append(func);
- else
- result.allFunctions.append(func);
- }
- }
+ std::remove_copy_if(allFunctions.cbegin(), allFunctions.cend(),
+ std::back_inserter(result.allFunctions), shouldSkip);
- std::sort(result.allFunctions.begin(), result.allFunctions.end(), functionSort);
+ std::stable_sort(result.allFunctions.begin(), result.allFunctions.end(), functionSort);
for (const auto &func : std::as_const(result.allFunctions)) {
if (func->isStatic())
{
if (link.type != QtXmlToSphinxLink::Reference || !isRelativeHtmlFile(link.linkRef))
return link;
- static const QString prefix = QStringLiteral("https://doc.qt.io/qt-")
+ static const QString prefix = "https://doc.qt.io/qt-"_L1
+ QString::number(QT_VERSION_MAJOR) + u'/';
QtXmlToSphinxLink resolved = link;
resolved.type = QtXmlToSphinxLink::External;
class DocParser;
struct DocGeneratorOptions;
struct GeneratorDocumentation;
+struct DocPackage;
/**
* The DocGenerator generates documentation from library being binded.
bool finishGeneration() override;
private:
- void writeEnums(TextStream &s, const AbstractMetaClassCPtr &cppClass) const;
+ void writeEnums(TextStream &s, const AbstractMetaEnumList &enums,
+ const QString &scope) const;
void writeFields(TextStream &s, const AbstractMetaClassCPtr &cppClass) const;
- static QString functionSignature(const AbstractMetaClassCPtr &cppClass,
- const AbstractMetaFunctionCPtr &func);
- void writeFunction(TextStream &s, const AbstractMetaClassCPtr &cppClass,
- const AbstractMetaFunctionCPtr &func, bool indexed = true);
+ void writeFunctions(TextStream &s, const AbstractMetaFunctionCList &funcs,
+ const AbstractMetaClassCPtr &cppClass, const QString &scope);
+ void writeFunction(TextStream &s, const AbstractMetaFunctionCPtr &func,
+ const AbstractMetaClassCPtr &cppClass = {},
+ const QString &scope = {}, bool indexed = true);
+ void writeFunctionDocumentation(TextStream &s, const AbstractMetaFunctionCPtr &func,
+ const DocModificationList &modifications,
+ const QString &scope) const;
void writeFunctionParametersType(TextStream &s, const AbstractMetaClassCPtr &cppClass,
const AbstractMetaFunctionCPtr &func) const;
static void writeFunctionToc(TextStream &s, const QString &title,
- const AbstractMetaClassCPtr &cppClass,
const AbstractMetaFunctionCList &functions);
static void writePropertyToc(TextStream &s,
- const GeneratorDocumentation &doc,
- const AbstractMetaClassCPtr &cppClass);
+ const GeneratorDocumentation &doc);
void writeProperties(TextStream &s,
const GeneratorDocumentation &doc,
const AbstractMetaClassCPtr &cppClass) const;
void writeParameterType(TextStream &s, const AbstractMetaClassCPtr &cppClass,
const AbstractMetaArgument &arg) const;
-
- void writeConstructors(TextStream &s,
- const AbstractMetaClassCPtr &cppClass,
- const AbstractMetaFunctionCList &constructors) const;
-
void writeFormattedText(TextStream &s, const QString &doc,
Documentation::Format format,
- const AbstractMetaClassCPtr &metaClass = {}) const;
+ const QString &scope = {}) const;
void writeFormattedBriefText(TextStream &s, const Documentation &doc,
- const AbstractMetaClassCPtr &metaclass = {}) const;
+ const QString &scope = {}) const;
void writeFormattedDetailedText(TextStream &s, const Documentation &doc,
- const AbstractMetaClassCPtr &metaclass = {}) const;
+ const QString &scope = {}) const;
bool writeInjectDocumentation(TextStream &s, TypeSystem::DocModificationMode mode,
- const AbstractMetaClassCPtr &cppClass,
- const AbstractMetaFunctionCPtr &func);
+ const AbstractMetaClassCPtr &cppClass) const;
+ bool writeInjectDocumentation(TextStream &s, TypeSystem::DocModificationMode mode,
+ const DocModificationList &modifications,
+ const AbstractMetaFunctionCPtr &func,
+ const QString &scope = {}) const;
+ bool writeDocModifications(TextStream &s, const DocModificationList &mods,
+ TypeSystem::DocModificationMode mode,
+ const QString &scope = {}) const;
static void writeDocSnips(TextStream &s, const CodeSnipList &codeSnips,
TypeSystem::CodeSnipPosition position, TypeSystem::Language language);
void writeModuleDocumentation();
+ void writeGlobals(const QString &package, const QString &fileName,
+ const DocPackage &docPackage);
void writeAdditionalDocumentation() const;
bool writeInheritanceFile();
static GeneratorDocumentation generatorDocumentation(const AbstractMetaClassCPtr &cppClass);
QStringList m_functionList;
- QMap<QString, QStringList> m_packages;
+ QMap<QString, DocPackage> m_packages;
QScopedPointer<DocParser> m_docParser;
static DocGeneratorOptions m_options;
};
using namespace Qt::StringLiterals;
-static inline QString nameAttribute() { return QStringLiteral("name"); }
-static inline QString titleAttribute() { return QStringLiteral("title"); }
-static inline QString fullTitleAttribute() { return QStringLiteral("fulltitle"); }
-
QString msgTagWarning(const QXmlStreamReader &reader, const QString &context,
const QString &tag, const QString &message)
{
return result;
}
-static const QString autoTranslatedPlaceholder = u"AUTO_GENERATED\n"_s;
-static const QString autoTranslatedNote =
-uR"(.. warning::
+constexpr auto autoTranslatedPlaceholder = "AUTO_GENERATED\n"_L1;
+constexpr auto autoTranslatedNote =
+R"(.. warning::
This section contains snippets that were automatically
translated from C++ to Python and may contain errors.
-)"_s;
+)"_L1;
void QtXmlToSphinx::setAutoTranslatedNote(QString *str) const
{
{
QString result = popOutputBuffer().simplified();
if (result.startsWith(u"**Warning:**"))
- result.replace(0, 12, QStringLiteral(".. warning:: "));
+ result.replace(0, 12, ".. warning:: "_L1);
else if (result.startsWith(u"**Note:**"))
- result.replace(0, 9, QStringLiteral(".. note:: "));
+ result.replace(0, 9, ".. note:: "_L1);
m_output << result << "\n\n";
}
}
}
-static inline QString functionLinkType() { return QStringLiteral("function"); }
-static inline QString classLinkType() { return QStringLiteral("class"); }
+constexpr auto functionLinkType = "function"_L1;
+constexpr auto classLinkType = "class"_L1;
static inline QString fixLinkType(QStringView type)
{
// TODO: create a flag PROPERTY-AS-FUNCTION to ask if the properties
// are recognized as such or not in the binding
if (type == u"property")
- return functionLinkType();
+ return functionLinkType;
if (type == u"typedef")
- return classLinkType();
+ return classLinkType;
return type.toString();
}
static inline QString linkSourceAttribute(const QString &type)
{
- if (type == functionLinkType() || type == classLinkType())
+ if (type == functionLinkType || type == classLinkType)
return u"raw"_s;
return type == u"enum" || type == u"page"
? type : u"href"_s;
const QString text = textR.toString();
if (m_seeAlsoContext.isNull()) {
const QString type = text.endsWith(u"()")
- ? functionLinkType() : classLinkType();
+ ? functionLinkType : classLinkType;
m_seeAlsoContext.reset(handleLinkStart(type, text));
}
handleLinkText(m_seeAlsoContext.data(), text);
}
}
-static inline QString fallbackPathAttribute() { return QStringLiteral("path"); }
+constexpr auto fallbackPathAttribute = "path"_L1;
template <class Indent> // const char*/class Indentor
void formatSnippet(TextStream &str, Indent indent, const QString &snippet)
QString location = reader.attributes().value(u"location"_s).toString();
QString identifier = reader.attributes().value(u"identifier"_s).toString();
QString fallbackPath;
- if (reader.attributes().hasAttribute(fallbackPathAttribute()))
- fallbackPath = reader.attributes().value(fallbackPathAttribute()).toString();
+ if (reader.attributes().hasAttribute(fallbackPathAttribute))
+ fallbackPath = reader.attributes().value(fallbackPathAttribute).toString();
QString errorMessage;
const Snippet snippet = readSnippetFromLocations(location, identifier,
if (type == u"external" || isHttpLink(ref)) {
result->type = QtXmlToSphinxLink::External;
- } else if (type == functionLinkType() && !m_context.isEmpty()) {
+ } else if (type == functionLinkType && !m_context.isEmpty()) {
result->type = QtXmlToSphinxLink::Method;
const auto rawlinklist = QStringView{result->linkRef}.split(u'.');
if (rawlinklist.size() == 1 || rawlinklist.constFirst() == m_context) {
} else {
result->linkRef = m_generator->expandFunction(result->linkRef);
}
- } else if (type == functionLinkType() && m_context.isEmpty()) {
+ } else if (type == functionLinkType && m_context.isEmpty()) {
result->type = QtXmlToSphinxLink::Function;
- } else if (type == classLinkType()) {
+ } else if (type == classLinkType) {
result->type = QtXmlToSphinxLink::Class;
result->linkRef = m_generator->expandClass(m_context, result->linkRef);
} else if (type == u"enum") {
m_output << disableIndent;
- const auto title = reader.attributes().value(titleAttribute());
+ const auto title = reader.attributes().value("title");
if (!title.isEmpty())
m_output << rstLabel(title.toString());
- const auto fullTitle = reader.attributes().value(fullTitleAttribute());
+ const auto fullTitle = reader.attributes().value("fulltitle");
const int size = fullTitle.isEmpty()
? writeEscapedRstText(m_output, title)
: writeEscapedRstText(m_output, fullTitle);
{
if (reader.tokenType() != QXmlStreamReader::StartElement)
return;
- const auto name = reader.attributes().value(nameAttribute());
+ const auto name = reader.attributes().value("name");
if (!name.isEmpty())
m_output << rstLabel(name.toString());
}
static const char shibokenErrorsOccurred[] = "Shiboken::Errors::occurred() != nullptr";
+static constexpr auto virtualMethodStaticReturnVar = "result"_L1;
+
+static constexpr auto sbkObjectTypeF = "SbkObject_TypeF()"_L1;
+static const char initInheritanceFunction[] = "initInheritance";
+
static QString mangleName(QString name)
{
if (name == u"None" || name == u"False" || name == u"True" || name == u"from")
struct sbkUnusedVariableCast
{
- explicit sbkUnusedVariableCast(QStringView name) : m_name(name) {}
+ explicit sbkUnusedVariableCast(QAnyStringView name) : m_name(name) {}
- const QStringView m_name;
+ const QAnyStringView m_name;
};
TextStream &operator<<(TextStream &str, const sbkUnusedVariableCast &c)
return str;
}
+struct pyTypeGetSlot
+{
+ explicit pyTypeGetSlot(QAnyStringView funcType, QAnyStringView typeObject,
+ QAnyStringView aSlot) :
+ m_funcType(funcType), m_typeObject(typeObject), m_slot(aSlot) {}
+
+ const QAnyStringView m_funcType;
+ const QAnyStringView m_typeObject;
+ const QAnyStringView m_slot;
+};
+
+TextStream &operator<<(TextStream &str, const pyTypeGetSlot &p)
+{
+ str << "reinterpret_cast<" << p.m_funcType << ">(PepType_GetSlot("
+ << p.m_typeObject << ", " << p.m_slot << "));\n";
+ return str;
+}
+
TextStream &operator<<(TextStream &s, CppGenerator::ErrorReturn r)
{
s << "return";
return s;
}
+static constexpr auto converterVar = "converter"_L1;
+
+struct registerConverterName
+{
+ explicit registerConverterName(QAnyStringView typeName,
+ QAnyStringView varName = converterVar) :
+ m_typeName(typeName), m_varName(varName) {}
+
+ QAnyStringView m_typeName;
+ QAnyStringView m_varName;
+};
+
+TextStream &operator<<(TextStream &s, const registerConverterName &r)
+{
+ s << "Shiboken::Conversions::registerConverterName(" << r.m_varName
+ << ", \"" << r.m_typeName << "\");\n";
+ return s;
+}
+
// Protocol function name / function parameters / return type
struct ProtocolEntry
{
u"PyObject*"_s},
{u"__msetitem__"_s,
u"PyObject *self, PyObject *_key, PyObject *_value"_s,
- intT()}};
+ intT}};
return result;
}
u"PyObject*"_s},
{u"__setitem__"_s,
u"PyObject *self, Py_ssize_t _i, PyObject *_value"_s,
- intT()},
+ intT},
{u"__getslice__"_s,
u"PyObject *self, Py_ssize_t _i1, Py_ssize_t _i2"_s,
u"PyObject*"_s},
{u"__setslice__"_s,
u"PyObject *self, Py_ssize_t _i1, Py_ssize_t _i2, PyObject *_value"_s,
- intT()},
+ intT},
{u"__contains__"_s,
u"PyObject *self, PyObject *_value"_s,
- intT()},
+ intT},
{u"__concat__"_s,
u"PyObject *self, PyObject *_other"_s,
u"PyObject*"_s}
writeVirtualMethodNative(s, func, maxOverrides++);
}
- if (!avoidProtectedHack() || !metaClass->hasPrivateDestructor()) {
- if (usePySideExtensions() && isQObject(metaClass))
- writeMetaObjectMethod(s, classContext);
+ if (shouldGenerateMetaObjectFunctions(metaClass))
+ writeMetaObjectMethod(s, classContext);
+ if (!avoidProtectedHack() || !metaClass->hasPrivateDestructor())
writeDestructorNative(s, classContext);
- }
}
+ for (const auto &f : metaClass->userAddedPythonOverrides())
+ writeUserAddedPythonOverride(s, f);
+
StringStream smd(TextStream::Language::Cpp);
StringStream md(TextStream::Language::Cpp);
StringStream signatureStream(TextStream::Language::Cpp);
}
// Determine the return statement (void or a result value).
-QString CppGenerator::virtualMethodReturn(TextStream &s, const ApiExtractorResult &api,
- const AbstractMetaFunctionCPtr &func,
- const FunctionModificationList &functionModifications)
+
+CppGenerator::VirtualMethodReturn
+ CppGenerator::virtualMethodReturn(const ApiExtractorResult &api,
+ const AbstractMetaFunctionCPtr &func,
+ const FunctionModificationList &functionModifications)
{
- if (func->isVoid())
- return u"return;"_s;
+ VirtualMethodReturn result;
+ if (func->isVoid()) {
+ result.statement = "return;"_L1;
+ return result;
+ }
+
+ result.statement = "return "_L1;
const AbstractMetaType &returnType = func->type();
for (const FunctionModification &mod : functionModifications) {
for (const ArgumentModification &argMod : mod.argument_mods()) {
if (argMod.index() == 0 && !argMod.replacedDefaultExpression().isEmpty()) {
- static const QRegularExpression regex(QStringLiteral("%(\\d+)"));
+ static const QRegularExpression regex("%(\\d+)"_L1);
Q_ASSERT(regex.isValid());
QString expr = argMod.replacedDefaultExpression();
for (int offset = 0; ; ) {
offset = match.capturedStart(1);
}
DefaultValue defaultReturnExpr(DefaultValue::Custom, expr);
- return u"return "_s + defaultReturnExpr.returnValue()
- + u';';
+ result.statement += defaultReturnExpr.returnValue() + u';';
+ return result;
}
}
}
errorMsg = msgCouldNotFindMinimalConstructor(errorMsg,
func->type().cppSignature(),
errorMessage);
- qCWarning(lcShiboken).noquote().nospace() << errorMsg;
- s << "\n#error " << errorMsg << '\n';
- }
- if (returnType.referenceType() == LValueReference) {
- s << "static " << returnType.typeEntry()->qualifiedCppName()
- << " result;\n";
- return u"return result;"_s;
+ throw Exception(errorMsg);
}
- return u"return "_s + defaultReturnExpr->returnValue()
- + u';';
+
+ result.needsReference = returnType.referenceType() == LValueReference;
+ result.statement += (result.needsReference
+ ? virtualMethodStaticReturnVar : defaultReturnExpr->returnValue()) + u';';
+ return result;
}
// Create an argument for Py_BuildValue() when writing virtual methods.
// PyObject_Vectorcall(): since 3.9
static const char vectorCallCondition[] =
- "#if !defined(PYPY_VERSION) && !defined(Py_LIMITED_API) && PY_VERSION_HEX >= 0x03090000\n";
+ "#if !defined(PYPY_VERSION) && !defined(Py_LIMITED_API)\n";
// PyObject_CallNoArgs(): since 3.9, stable API since 3.10
static const char noArgsCallCondition[] =
- "#if !defined(PYPY_VERSION) && ((defined(Py_LIMITED_API) && Py_LIMITED_API >= 0x030A0000) || (!defined(Py_LIMITED_API) && PY_VERSION_HEX >= 0x03090000))\n";
+ "#if !defined(PYPY_VERSION) && ((defined(Py_LIMITED_API) && Py_LIMITED_API >= 0x030A0000) || !defined(Py_LIMITED_API))\n";
static const char inverseNoArgsCallCondition[] =
- "#if defined(PYPY_VERSION) || (defined(Py_LIMITED_API) && Py_LIMITED_API < 0x030A0000) || (!defined(Py_LIMITED_API) && PY_VERSION_HEX < 0x03090000)\n";
+ "#if defined(PYPY_VERSION) || (defined(Py_LIMITED_API) && Py_LIMITED_API < 0x030A0000)\n";
+
+static inline void writeVirtualMethodStaticReturnVar(TextStream &s, const AbstractMetaFunctionCPtr &func)
+{
+ s << "static " << func->type().typeEntry()->qualifiedCppName() << ' '
+ << virtualMethodStaticReturnVar << ";\n";
+}
+
+static void writeFuncNameVar(TextStream &s, const AbstractMetaFunctionCPtr &func,
+ const QString &funcName)
+{
+ // PYSIDE-1019: Add info about properties
+ int propFlag = 0;
+ if (func->isPropertyReader())
+ propFlag |= 1;
+ if (func->isPropertyWriter())
+ propFlag |= 2;
+ if (propFlag && func->isStatic())
+ propFlag |= 4;
+ QString propStr;
+ if (propFlag != 90)
+ propStr = QString::number(propFlag) + u':';
+
+ if (propFlag != 0)
+ s << "// This method belongs to a property.\n";
+ s << "static const char *funcName = \"";
+ if (propFlag != 0)
+ s << propFlag << ':';
+ s << funcName << "\";\n";
+}
void CppGenerator::writeVirtualMethodNative(TextStream &s,
const AbstractMetaFunctionCPtr &func,
Generator::OriginalTypeDescription)
<< "\n{\n" << indent;
- const QString returnStatement = virtualMethodReturn(s, api(), func,
- func->modifications());
+ const auto returnStatement = virtualMethodReturn(api(), func,
+ func->modifications());
+
+ if (returnStatement.needsReference)
+ writeVirtualMethodStaticReturnVar(s, func);
const bool isAbstract = func->isAbstract();
if (isAbstract && func->isModifiedRemoved()) {
qCWarning(lcShiboken, "%s", qPrintable(msgPureVirtualFunctionRemoved(func.get())));
- s << returnStatement << '\n' << outdent << "}\n\n";
+ s << returnStatement.statement << '\n' << outdent << "}\n\n";
return;
}
s << "if (m_PyMethodCache[" << cacheIndex << "])" << (multi_line ? " {\n" : "\n")
<< indent;
writeVirtualMethodCppCall(s, func, funcName, snips, lastArg, retType,
- returnStatement, false);
+ returnStatement.statement, false);
s << outdent;
if (multi_line)
s << "}\n";
// Get out of virtual method call if someone already threw an error.
s << "if (" << shibokenErrorsOccurred << ")\n" << indent
- << returnStatement << '\n' << outdent;
-
- // PYSIDE-1019: Add info about properties
- int propFlag = 0;
- if (func->isPropertyReader())
- propFlag |= 1;
- if (func->isPropertyWriter())
- propFlag |= 2;
- if (propFlag && func->isStatic())
- propFlag |= 4;
- QString propStr;
- if (propFlag)
- propStr = QString::number(propFlag) + u':';
+ << returnStatement.statement << '\n' << outdent;
s << "static PyObject *nameCache[2] = {};\n";
- if (propFlag)
- s << "// This method belongs to a property.\n";
- s << "static const char *funcName = \"" << propStr << funcName << "\";\n"
- << "Shiboken::AutoDecRef " << PYTHON_OVERRIDE_VAR
+ writeFuncNameVar(s, func, funcName);
+ s << "Shiboken::AutoDecRef " << PYTHON_OVERRIDE_VAR
<< "(Shiboken::BindingManager::instance().getOverride(this, nameCache, funcName));\n"
<< "if (" << PYTHON_OVERRIDE_VAR << ".isNull()) {\n" << indent;
if (useOverrideCaching(func->ownerClass()))
s << "m_PyMethodCache[" << cacheIndex << "] = true;\n";
writeVirtualMethodCppCall(s, func, funcName, snips, lastArg, retType,
- returnStatement, true);
+ returnStatement.statement, true);
s << outdent << "}\n\n"; //WS
+ if (!snips.isEmpty()) {
+ writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionPyOverride,
+ TypeSystem::ShellCode, func, false, lastArg);
+ }
+
+ writeVirtualMethodPythonOverride(s, func, snips, returnStatement);
+}
+
+void CppGenerator::writeVirtualMethodPythonOverride(TextStream &s,
+ const AbstractMetaFunctionCPtr &func,
+ const CodeSnipList &snips,
+ const VirtualMethodReturn &returnStatement) const
+{
writeConversionRule(s, func, TypeSystem::TargetLangCode, false);
bool invalidateReturn = false;
auto arguments = func->arguments();
auto removedEnd = std::stable_partition(arguments.begin(), arguments.end(),
isArgumentNotRemoved);
- if (isAbstract) { // Base function is not called, indicate unused arguments.
+ if (func->isAbstract()) { // Base function is not called, indicate unused arguments.
for (auto it = removedEnd; it != arguments.end(); ++it)
s << sbkUnusedVariableCast(it->name());
}
s << "if (" << PYTHON_RETURN_VAR << ".isNull()) {\n" << indent
<< "// An error happened in python code!\n"
<< "Shiboken::Errors::storeErrorOrPrint();\n"
- << returnStatement << "\n" << outdent
+ << returnStatement.statement << "\n" << outdent
<< "}\n";
if (invalidateReturn) {
if (!func->isVoid()) {
- if (func->modifiedTypeName() != cPyObjectT()) {
+ if (func->modifiedTypeName() != cPyObjectT) {
s << "// Check return type\n";
<< func->ownerClass()->name() << "\", funcName, "
<< getVirtualFunctionReturnTypeName(func) << ", "
<< "Py_TYPE(" << PYTHON_RETURN_VAR << ")->tp_name);\n"
- << returnStatement << '\n' << outdent
+ << returnStatement.statement << '\n' << outdent
<< "}\n";
} else {
<< func->ownerClass()->name() << "\", funcName, "
<< getVirtualFunctionReturnTypeName(func) << ", "
<< "Py_TYPE(" << PYTHON_RETURN_VAR << ")->tp_name);\n"
- << returnStatement << '\n' << outdent
+ << returnStatement.statement << '\n' << outdent
<< "}\n";
}
if (!func->isVoid()) {
s << "return ";
+ TypeEntryCPtr retType = func->type().typeEntry();
if (avoidProtectedHack() && retType->isEnum()) {
auto metaEnum = api().findAbstractMetaEnum(retType);
bool isProtectedEnum = metaEnum.has_value() && metaEnum->isProtected();
s << outdent << "}\n\n";
}
+void CppGenerator::writeUserAddedPythonOverride(TextStream &s,
+ const AbstractMetaFunctionCPtr &func) const
+{
+ TypeEntryCPtr retType = func->type().typeEntry();
+ const QString funcName = func->isOperatorOverload()
+ ? pythonOperatorFunctionName(func) : func->definitionNames().constFirst();
+
+ const CodeSnipList snips = func->hasInjectedCode()
+ ? func->injectedCodeSnips() : CodeSnipList();
+
+ QString prefix = wrapperName(func->ownerClass()) + u"::"_s;
+ s << '\n' << functionSignature(func, prefix, QString(), Generator::SkipDefaultValues |
+ Generator::OriginalTypeDescription)
+ << "\n{\n" << indent << sbkUnusedVariableCast("gil");
+
+ writeFuncNameVar(s, func, funcName);
+
+ const auto returnStatement = virtualMethodReturn(api(), func,
+ func->modifications());
+ writeVirtualMethodPythonOverride(s, func, snips, returnStatement);
+}
+
void CppGenerator::writeMetaObjectMethod(TextStream &s,
const GeneratorContext &classContext) const
{
<< "if (pyOut) {\n" << indent
<< "Py_INCREF(pyOut);\nreturn pyOut;\n" << outdent
<< "}\n"
- << "bool changedTypeName = false;\n"
+ << "bool exactType = false;\n"
<< "auto *tCppIn = reinterpret_cast<const " << typeName << R"( *>(cppIn);
const char *typeName = )";
else
c << nameFunc << "(tCppIn);\n";
c << R"(auto *sbkType = Shiboken::ObjectType::typeForTypeName(typeName);
-if (sbkType != nullptr && Shiboken::ObjectType::hasSpecialCastFunction(sbkType)) {
- typeName = Shiboken::typeNameOf(typeid(*tCppIn).name());
- changedTypeName = true;
-}
+if (sbkType != nullptr && Shiboken::ObjectType::hasSpecialCastFunction(sbkType))
+ exactType = true;
)"
<< "PyObject *result = Shiboken::Object::newObject(" << cpythonType
- << R"(, const_cast<void *>(cppIn), false, /* exactType */ changedTypeName, typeName);
-if (changedTypeName)
- delete [] typeName;
+ << R"(, const_cast<void *>(cppIn), false, exactType, typeName);
return result;)";
}
std::swap(targetTypeName, sourceTypeName);
c << "auto *source = reinterpret_cast<const " << typeName << " *>(cppIn);\n";
}
c << "return Shiboken::Object::newObject(" << cpythonType
- << ", new ::" << classContext.effectiveClassName() << '('
+ << ", new " << globalScopePrefix(classContext) << classContext.effectiveClassName() << '('
<< (isUniquePointer ? "std::move(*source)" : "*source")
<< "), true, true);";
writeCppToPythonFunction(s, c.toString(), sourceTypeName, targetTypeName);
s << "// Python to C++ copy conversion.\n";
sourceTypeName = metaClass->name();
- targetTypeName = sourceTypeName + QStringLiteral("_COPY");
+ targetTypeName = sourceTypeName + "_COPY"_L1;
c.clear();
QString pyInVariable = u"pyIn"_s;
auto writeConversions = [&s](const QString &signature)
{
- s << "Shiboken::Conversions::registerConverterName(converter, \"" << signature << "\");\n"
- << "Shiboken::Conversions::registerConverterName(converter, \"" << signature << "*\");\n"
- << "Shiboken::Conversions::registerConverterName(converter, \"" << signature << "&\");\n";
+ s << registerConverterName(signature) << registerConverterName(signature + u'*')
+ << registerConverterName(signature + u'&');
};
auto writeConversionsForType = [writeConversions](const QString &fullTypeName)
writeConversionsForType(smartPointerType);
}
- s << "Shiboken::Conversions::registerConverterName(converter, typeid(::";
+ s << "Shiboken::Conversions::registerConverterName(converter, typeid(" << m_gsp;
QString qualifiedCppNameInvocation;
if (!classContext.forSmartPointer())
qualifiedCppNameInvocation = metaClass->qualifiedCppName();
s << qualifiedCppNameInvocation << ").name());\n";
if (classContext.useWrapper()) {
- s << "Shiboken::Conversions::registerConverterName(converter, typeid(::"
+ s << "Shiboken::Conversions::registerConverterName(converter, typeid("
<< classContext.wrapperName() << ").name());\n";
}
if (rfunc->isConstructor()) {
// Check if the right constructor was called.
if (!ownerClass->hasPrivateDestructor()) {
- s << "if (Shiboken::Object::isUserType(self) && !Shiboken::ObjectType::canCallConstructor(self->ob_type, Shiboken::SbkType< ::";
+ s << "if (Shiboken::Object::isUserType(self) && "
+ << "!Shiboken::ObjectType::canCallConstructor(self->ob_type, Shiboken::SbkType< "
+ << m_gsp;
QString qualifiedCppName;
if (!context.forSmartPointer())
qualifiedCppName = ownerClass->qualifiedCppName();
s << qualifiedCppName << " >()))\n" << indent << errorReturn << outdent << '\n';
}
// Declare pointer for the underlying C++ object.
- s << "::" << context.effectiveClassName() << " *cptr{};\n";
+ s << globalScopePrefix(context) << context.effectiveClassName() << " *cptr{};\n";
initPythonArguments = maxArgs > 0;
s << cpythonFunctionName(rfunc)
<< "(PyObject *self, PyObject *args, PyObject *kwds)\n{\n" << indent;
if (overloadData.maxArgs() == 0 || metaClass->isAbstract())
- s << sbkUnusedVariableCast(u"args"_s);
- s << sbkUnusedVariableCast(u"kwds"_s);
+ s << sbkUnusedVariableCast("args");
+ s << sbkUnusedVariableCast("kwds");
const bool needsMetaObject = usePySideExtensions() && isQObject(metaClass);
if (needsMetaObject)
if (metaClass->isAbstract()) {
// C++ Wrapper disabled: Abstract C++ class cannot be instantiated.
if (metaClass->typeEntry()->typeFlags().testFlag(ComplexTypeEntry::DisableWrapper)) {
- s << sbkUnusedVariableCast(u"sbkSelf"_s)
- << sbkUnusedVariableCast(u"type"_s)
- << sbkUnusedVariableCast(u"myType"_s);
+ s << sbkUnusedVariableCast("sbkSelf")
+ << sbkUnusedVariableCast("type")
+ << sbkUnusedVariableCast("myType");
if (needsMetaObject)
- s << sbkUnusedVariableCast(u"metaObject"_s);
+ s << sbkUnusedVariableCast("metaObject");
s << "Shiboken::Errors::setInstantiateAbstractClassDisabledWrapper(\""
<< metaClass->qualifiedCppName() << "\");\n" << errorReturn << outdent
<< "}\n\n";
const QString typeName = classContext.forSmartPointer()
? classContext.preciseType().cppSignature() : metaClass->qualifiedCppName();
s << "if (" << shibokenErrorsOccurred
- << " || !Shiboken::Object::setCppPointer(sbkSelf, Shiboken::SbkType< ::"
- << typeName << " >(), cptr)) {\n"
+ << " || !Shiboken::Object::setCppPointer(sbkSelf, Shiboken::SbkType< "
+ << globalScopePrefix(classContext) << typeName << " >(), cptr)) {\n"
<< indent << "delete cptr;\n" << errorReturn << outdent
<< "}\n";
if (overloadData.maxArgs() > 0)
}
s << ")\n{\n" << indent;
if (rfunc->ownerClass() == nullptr || overloadData.hasStaticFunction())
- s << sbkUnusedVariableCast(u"self");
+ s << sbkUnusedVariableCast(PYTHON_SELF_VAR);
if (hasKwdArgs)
- s << sbkUnusedVariableCast(u"kwds");
+ s << sbkUnusedVariableCast("kwds");
writeMethodWrapperPreamble(s, overloadData, classContext);
ErrorReturn errorReturn)
{
const auto rfunc = overloadData.referenceFunction();
- s << "PyTuple_GET_SIZE(args);\n" << sbkUnusedVariableCast(u"numArgs"_s);
+ s << "PyTuple_GET_SIZE(args);\n" << sbkUnusedVariableCast("numArgs");
int minArgs = overloadData.minArgs();
int maxArgs = overloadData.maxArgs();
return;
}
- static const QString pythonSelfVar = u"self"_s;
if (useWrapperClass)
s << "static_cast<" << className << " *>(";
- s << cpythonWrapperCPtr(context.metaClass(), pythonSelfVar);
+ s << cpythonWrapperCPtr(context.metaClass(), PYTHON_SELF_VAR);
if (useWrapperClass)
s << ')';
}
const QString className = useWrapperClass
? context.wrapperName() : getFullTypeName(metaClass);
- writeInvalidPyObjectCheck(s, u"self"_s, errorReturn);
+ writeInvalidPyObjectCheck(s, PYTHON_SELF_VAR, errorReturn);
if (flags.testFlag(CppSelfAsReference)) {
writeCppSelfVarDef(s, flags);
{
switch (nestedArrayTypes.size()) {
case 1:
- return QStringLiteral("Shiboken::Conversions::ArrayHandle<")
+ return "Shiboken::Conversions::ArrayHandle<"_L1
+ nestedArrayTypes.constLast().minimalSignature() + u'>';
case 2:
- return QStringLiteral("Shiboken::Conversions::Array2Handle<")
+ return "Shiboken::Conversions::Array2Handle<"_L1
+ nestedArrayTypes.constLast().minimalSignature()
- + QStringLiteral(", ")
+ + ", "_L1
+ QString::number(nestedArrayTypes.constFirst().arrayElementCount())
+ u'>';
}
int sequenceArgCount = 0;
while (od && !od->argType().isVarargs()) {
const bool typeReplacedByPyObject = od->isTypeModified()
- && od->modifiedArgType().name() == cPyObjectT();
+ && od->modifiedArgType().name() == cPyObjectT;
if (!typeReplacedByPyObject) {
if (usePyArgs)
pyArgName = pythonArgsAt(od->argPos());
continue;
auto argType = getArgumentType(func, argIdx);
int argPos = argIdx - removedArgs;
- QString argName = CPP_ARG(argPos);
QString pyArgName = usePyArgs ? pythonArgsAt(argPos) : PYTHON_ARG;
indirections[argIdx] =
- writeArgumentConversion(s, argType, argName, pyArgName, errorReturn,
+ writeArgumentConversion(s, argType, CPP_ARG_N(argPos), pyArgName, errorReturn,
func->implementingClass(), arg.defaultValueExpression(),
func->isUserAdded());
}
QString CppGenerator::containerNativeToTargetTypeName(const ContainerTypeEntryCPtr &type)
{
QString result = type->targetLangApiName();
- if (result != cPyObjectT()) {
+ if (result != cPyObjectT) {
result = containerCpythonBaseName(type);
- if (result == cPySequenceT())
- result = cPyListT();
+ if (result == cPySequenceT)
+ result = cPyListT;
}
return result;
}
<< "return Shiboken::Conversions::nonePythonToCppNullPtr;\n" << outdent;
} else {
if (!condition.contains(u"pyIn"))
- s << sbkUnusedVariableCast(u"pyIn");
+ s << sbkUnusedVariableCast("pyIn");
}
s << "if (" << condition << ")\n" << indent
<< "return " << pythonToCppFuncName << ";\n" << outdent
{
switch (argIndex) {
case -1:
- return u"self"_s;
+ return PYTHON_SELF_VAR;
case 0:
return PYTHON_RETURN_VAR;
case 1: { // Single argument?
const int idx = arg.argumentIndex() - removedArgs;
const auto deRef = argumentIndirections.at(i);
QString argName = AbstractMetaType::dereferencePrefix(deRef)
- + CPP_ARG(idx);
+ + CPP_ARG_N(idx);
userArgs.append(argName);
}
}
Q_ASSERT(owner == context.metaClass());
if (func->functionType() == AbstractMetaFunction::CopyConstructorFunction
&& maxArgs == 1) {
- mc << "new ::" << context.effectiveClassName()
+ mc << "new " << globalScopePrefix(context) << context.effectiveClassName()
<< "(*" << CPP_ARG0 << ')';
} else {
const QString ctorCall = context.effectiveClassName() + u'('
if (usePySideExtensions() && isQObject(owner)) {
s << "void *addr = PySide::nextQObjectMemoryAddr();\n";
uva << "if (addr != nullptr) {\n" << indent
- << "cptr = new (addr) ::" << ctorCall << ";\n"
- << "PySide::setNextQObjectMemoryAddr(nullptr);\n" << outdent
+ << "cptr = new (addr) " << globalScopePrefix(context) << ctorCall
+ << ";\nPySide::setNextQObjectMemoryAddr(nullptr);\n" << outdent
<< "} else {\n" << indent
- << "cptr = new ::" << ctorCall << ";\n"
+ << "cptr = new " << globalScopePrefix(context) << ctorCall << ";\n"
<< outdent << "}\n";
} else {
- mc << "new ::" << ctorCall;
+ mc << "new " << globalScopePrefix(context) << ctorCall;
}
}
} else {
const bool hasWrapper = shouldGenerateCppWrapper(ownerClass);
if (!avoidProtectedHack() || !func->isProtected() || !hasWrapper) {
if (func->isStatic()) {
- mc << "::" << methodCallClassName << "::";
+ mc << m_gsp << methodCallClassName << "::";
} else {
const QString cppSelfVar = CPP_SELF_VAR;
const QString selfVarCast = func->ownerClass() == func->implementingClass()
+ u" *>("_s + cppSelfVar + u')';
if (func->isConstant()) {
if (avoidProtectedHack()) {
- mc << "const_cast<const ::";
+ mc << "const_cast<const " << globalScopePrefix(context);
if (ownerClass->cppWrapper().testFlag(AbstractMetaClass::CppProtectedHackWrapper)) {
// PYSIDE-500: Need a special wrapper cast when inherited
const QString selfWrapCast = ownerClass == func->implementingClass()
mc << " *>(" << selfVarCast << ")->";
}
} else {
- mc << "const_cast<const ::" << methodCallClassName;
+ mc << "const_cast<const " << m_gsp << methodCallClassName;
mc << " *>(" << selfVarCast << ")->";
}
} else {
if (!func->isAbstract())
mc << (func->isProtected() ? wrapperName(func->ownerClass()) :
- u"::"_s
- + methodCallClassName) << "::";
+ m_gsp + methodCallClassName) << "::";
mc << func->originalName() << "_protected";
}
} else {
QString className = metaClass->qualifiedCppName();
s << "static void * " << cpythonSpecialCastFunctionName(metaClass)
<< "(void *obj, PyTypeObject *desiredType)\n{\n" << indent
- << "auto me = reinterpret_cast< ::" << className << " *>(obj);\n";
+ << "auto me = reinterpret_cast< " << m_gsp << className << " *>(obj);\n";
bool firstClass = true;
const auto &allAncestors = metaClass->allTypeSystemAncestors();
for (const auto &baseClass : allAncestors) {
<< converter << " = Shiboken::Conversions::createConverter(";
if (!type->hasTargetLangApiType())
s << "nullptr";
- else if (type->targetLangApiName() == cPyObjectT())
+ else if (type->targetLangApiName() == cPyObjectT)
s << "&PyBaseObject_Type";
else
s << '&' << type->targetLangApiName() << "_Type";
QString typeName = fixedCppTypeName(type);
s << ", " << cppToPythonFunctionName(typeName, typeName) << ");\n"
- << "Shiboken::Conversions::registerConverterName(" << converter << ", \""
- << type->qualifiedCppName() << "\");\n";
+ << registerConverterName(type->qualifiedCppName(), converter);
writeCustomConverterRegister(s, customConversion, converter);
}
-static void registerEnumConverterScopes(TextStream &s, QString signature)
+static void registerConverterInScopes(TextStream &s, QStringView signature,
+ QAnyStringView varName = converterVar)
{
while (true) {
- s << "Shiboken::Conversions::registerConverterName(converter, \""
- << signature << "\");\n";
- const auto qualifierPos = signature.indexOf(u"::");
- if (qualifierPos != -1)
- signature.remove(0, qualifierPos + 2);
- else
+ s << registerConverterName(signature, varName);
+ const auto qualifierPos = signature.indexOf("::"_L1);
+ if (qualifierPos == -1)
break;
+ signature = signature.sliced(qualifierPos + 2);
}
}
s << "Shiboken::Enum::setTypeConverter(" << enumPythonVar
<< ", converter);\n";
- registerEnumConverterScopes(s, enumType->qualifiedCppName());
+ registerConverterInScopes(s, enumType->qualifiedCppName());
if (auto flags = enumType->flags())
s << "// Register converter for flag '" << flags->qualifiedCppName() << "'.\n"
- << "Shiboken::Conversions::registerConverterName(converter, \""
- << flags->name() << "\");\n";
+ << registerConverterName(flags->name()) // QMetaType
+ << registerConverterName(flags->originalName()); // Signals with flags
+
s << outdent << "}\n";
}
QString CppGenerator::writeContainerConverterInitialization(TextStream &s,
- const AbstractMetaType &type)
+ const AbstractMetaType &type,
+ const ApiExtractorResult &api)
{
- QByteArray cppSignature = QMetaObject::normalizedSignature(type.cppSignature().toUtf8());
+ const auto cppSignature =
+ QString::fromUtf8(QMetaObject::normalizedSignature(type.cppSignature().toUtf8()));
s << "// Register converter for type '" << cppSignature << "'.\n";
- QString converter = converterObject(type);
+ const QString converter = converterObject(type);
s << converter << " = Shiboken::Conversions::createConverter(";
Q_ASSERT(type.typeEntry()->isContainer());
const auto typeEntry = std::static_pointer_cast<const ContainerTypeEntry>(type.typeEntry());
const QString targetTypeName = containerNativeToTargetTypeName(typeEntry);
- if (targetTypeName == cPyObjectT()) {
+ if (targetTypeName == cPyObjectT) {
s << "&PyBaseObject_Type";
} else {
s << '&' << targetTypeName << "_Type";
const QString typeName = fixedCppTypeName(type);
s << ", " << cppToPythonFunctionName(typeName, targetTypeName) << ");\n";
+ s << registerConverterName(cppSignature, converter);
+ if (usePySideExtensions() && cppSignature.startsWith("const "_L1)
+ && cppSignature.endsWith(u'&')) {
+ auto underlyingType = QStringView{cppSignature}.sliced(6, cppSignature.size() - 7);
+ s << registerConverterName(underlyingType, converter);
+ }
+
for (const auto &conv : typeEntry->customConversion()->targetToNativeConversions()) {
const QString &sourceTypeName = conv.sourceTypeName();
QString toCpp = pythonToCppFunctionName(sourceTypeName, typeName);
QString isConv = convertibleToCppFunctionName(sourceTypeName, typeName);
- s << "Shiboken::Conversions::registerConverterName(" << converter
- << ", \"" << cppSignature << "\");\n";
- if (usePySideExtensions() && cppSignature.startsWith("const ")
- && cppSignature.endsWith("&")) {
- cppSignature.chop(1);
- cppSignature.remove(0, sizeof("const ") / sizeof(char) - 1);
- s << "Shiboken::Conversions::registerConverterName(" << converter
- << ", \"" << cppSignature << "\");\n";
- }
writeAddPythonToCppConversion(s, converter, toCpp, isConv);
}
+
+ auto typedefItPair = api.typedefTargetToName().equal_range(type.cppSignature());
+ if (typedefItPair.first != typedefItPair.second) {
+ auto *typeDb = TypeDatabase::instance();
+ s << "// Register converters for type aliases of " << cppSignature << "'.\n";
+ for (auto it = typedefItPair.first; it != typedefItPair.second; ++it) {
+ if (!typeDb->findType(it.value()))
+ s << registerConverterName(it.value(), converter);
+ }
+ }
+
return converter;
}
+QString CppGenerator::typeInitStruct(const TypeEntryCPtr &te)
+{
+ return cppApiVariableName(te->targetLangPackage()) + u'['
+ + getTypeIndexVariableName(te) + u']';
+}
+
void CppGenerator::writeExtendedConverterInitialization(TextStream &s,
const TypeEntryCPtr &externalType,
const AbstractMetaClassCList &conversions)
s << "// Extended implicit conversions for " << externalType->qualifiedTargetLangName()
<< ".\n";
for (const auto &sourceClass : conversions) {
- const QString converterVar = cppApiVariableName(externalType->targetLangPackage()) + u'['
- + getTypeIndexVariableName(externalType) + u']';
QString sourceTypeName = fixedCppTypeName(sourceClass->typeEntry());
QString targetTypeName = fixedCppTypeName(externalType);
QString toCpp = pythonToCppFunctionName(sourceTypeName, targetTypeName);
QString isConv = convertibleToCppFunctionName(sourceTypeName, targetTypeName);
- writeAddPythonToCppConversion(s, converterVar, toCpp, isConv);
+ if (!externalType->isPrimitive())
+ s << cpythonTypeNameExt(externalType) << ";\n";
+ writeAddPythonToCppConversion(s, typeInitStruct(externalType), toCpp, isConv);
}
}
struct pyTypeSlotEntry
{
- explicit pyTypeSlotEntry(QStringView name, QStringView function) :
+ explicit pyTypeSlotEntry(QAnyStringView name, QAnyStringView function) :
m_name(name), m_function(function) {}
- QStringView m_name;
- QStringView m_function;
+ QAnyStringView m_name;
+ QAnyStringView m_function;
};
TextStream &operator<<(TextStream &str, const pyTypeSlotEntry &e)
QString tp_hash;
QString tp_call;
const QString className = chopType(cpythonTypeName(metaClass));
- QString baseClassName;
AbstractMetaFunctionCList ctors;
const auto &allCtors = metaClass->queryFunctions(FunctionQueryOption::AnyConstructor);
for (const auto &f : allCtors) {
}
}
- if (!metaClass->baseClass())
- baseClassName = u"SbkObject_TypeF()"_s;
-
bool onlyPrivCtor = !metaClass->hasNonPrivateConstructor();
const bool isQApp = usePySideExtensions()
if (hasHashFunction(metaClass))
tp_hash = u'&' + cpythonBaseName(metaClass) + u"_HashFunc"_s;
- const auto callOp = metaClass->findFunction(u"operator()");
+ const auto callOp = metaClass->findFunction("operator()");
if (callOp && !callOp->isModifiedRemoved())
tp_call = u'&' + cpythonFunctionName(callOp);
<< "{\n" << indent << "return " << typePtr << ";\n" << outdent
<< "}\n\nstatic PyType_Slot " << className << "_slots[] = {\n" << indent
<< "{Py_tp_base, nullptr}, // inserted by introduceWrapperType\n"
- << pyTypeSlotEntry(u"Py_tp_dealloc", tp_dealloc)
- << pyTypeSlotEntry(u"Py_tp_repr", m_tpFuncs.value(REPR_FUNCTION))
- << pyTypeSlotEntry(u"Py_tp_hash", tp_hash)
- << pyTypeSlotEntry(u"Py_tp_call", tp_call)
- << pyTypeSlotEntry(u"Py_tp_str", m_tpFuncs.value(u"__str__"_s))
- << pyTypeSlotEntry(u"Py_tp_getattro", tp_getattro)
- << pyTypeSlotEntry(u"Py_tp_setattro", tp_setattro)
- << pyTypeSlotEntry(u"Py_tp_traverse", className + u"_traverse"_s)
- << pyTypeSlotEntry(u"Py_tp_clear", className + u"_clear"_s)
- << pyTypeSlotEntry(u"Py_tp_richcompare", tp_richcompare)
- << pyTypeSlotEntry(u"Py_tp_iter", m_tpFuncs.value(u"__iter__"_s))
- << pyTypeSlotEntry(u"Py_tp_iternext", m_tpFuncs.value(u"__next__"_s))
- << pyTypeSlotEntry(u"Py_tp_methods", className + u"_methods"_s)
- << pyTypeSlotEntry(u"Py_tp_getset", tp_getset)
- << pyTypeSlotEntry(u"Py_tp_init", tp_init)
- << pyTypeSlotEntry(u"Py_tp_new", tp_new);
+ << pyTypeSlotEntry("Py_tp_dealloc", tp_dealloc)
+ << pyTypeSlotEntry("Py_tp_repr", m_tpFuncs.value(REPR_FUNCTION))
+ << pyTypeSlotEntry("Py_tp_hash", tp_hash)
+ << pyTypeSlotEntry("Py_tp_call", tp_call)
+ << pyTypeSlotEntry("Py_tp_str", m_tpFuncs.value(u"__str__"_s))
+ << pyTypeSlotEntry("Py_tp_getattro", tp_getattro)
+ << pyTypeSlotEntry("Py_tp_setattro", tp_setattro)
+ << pyTypeSlotEntry("Py_tp_traverse", className + u"_traverse"_s)
+ << pyTypeSlotEntry("Py_tp_clear", className + u"_clear"_s)
+ << pyTypeSlotEntry("Py_tp_richcompare", tp_richcompare)
+ << pyTypeSlotEntry("Py_tp_iter", m_tpFuncs.value(u"__iter__"_s))
+ << pyTypeSlotEntry("Py_tp_iternext", m_tpFuncs.value(u"__next__"_s))
+ << pyTypeSlotEntry("Py_tp_methods", className + u"_methods"_s)
+ << pyTypeSlotEntry("Py_tp_getset", tp_getset)
+ << pyTypeSlotEntry("Py_tp_init", tp_init)
+ << pyTypeSlotEntry("Py_tp_new", tp_new);
if (supportsSequenceProtocol(metaClass)) {
s << "// type supports sequence protocol\n";
writeTypeAsSequenceDefinition(s, metaClass);
QString baseName = cpythonBaseName(metaClass);
s << "static int " << baseName
<< "_traverse(PyObject *self, visitproc visit, void *arg)\n{\n" << indent
- << "return SbkObject_TypeF()->tp_traverse(self, visit, arg);\n"
+ << "auto traverseProc = "
+ << pyTypeGetSlot("traverseproc", sbkObjectTypeF, "Py_tp_traverse") << ";\n"
+ << "return traverseProc(self, visit, arg);\n"
<< outdent << "}\n";
}
{
QString baseName = cpythonBaseName(metaClass);
s << "static int " << baseName << "_clear(PyObject *self)\n{\n" << indent
- << "return reinterpret_cast<PyTypeObject *>(SbkObject_TypeF())->tp_clear(self);\n"
+ << "auto clearProc = "
+ << pyTypeGetSlot("inquiry", sbkObjectTypeF, "Py_tp_clear") << ";\n"
+ << "return clearProc(self);\n"
<< outdent << "}\n";
}
{
writeGetterFunctionStart(s, cpythonGetterFunctionName(property, context.metaClass()));
writeCppSelfDefinition(s, context);
- const QString value = QStringLiteral("value");
+ const QString value = "value"_L1;
s << "auto " << value << " = " << CPP_SELF_VAR << "->" << property.read() << "();\n"
<< "auto *pyResult = ";
writeToPythonConversion(s, property.type(), context.metaClass(), value);
return result;
}
+QString CppGenerator::pythonSignature(const AbstractMetaType &type) const
+{
+ if (type.isSmartPointer() && !type.instantiations().isEmpty()) {
+ const auto ste = std::static_pointer_cast<const SmartPointerTypeEntry>(type.typeEntry());
+ const auto instantiationTe = type.instantiations().constFirst().typeEntry();
+ if (auto opt = api().findSmartPointerInstantiation(ste, instantiationTe))
+ return opt->specialized->typeEntry()->qualifiedTargetLangName();
+ }
+ return type.pythonSignature();
+}
+
// Format the type signature of a function parameter
QString CppGenerator::signatureParameter(const AbstractMetaArgument &arg) const
{
metaType = *viewOn;
s << arg.name() << ':';
- QStringList signatures(metaType.pythonSignature());
+ QStringList signatures(pythonSignature(metaType));
// Implicit conversions (C++): Check for converting constructors
// "QColor(Qt::GlobalColor)" or conversion operators
const AbstractMetaFunctionCList conversions =
api().implicitConversions(metaType);
for (const auto &f : conversions) {
- if (f->isConstructor() && !f->arguments().isEmpty())
- signatures << f->arguments().constFirst().type().pythonSignature();
- else if (f->isConversionOperator())
+ if (f->isConstructor() && !f->arguments().isEmpty()) {
+ // PYSIDE-2712: modified types from converting constructors are not always correct
+ // candidates if they are modified by the type system reference
+ if (!f->arguments().constFirst().isTypeModified()) {
+ signatures << pythonSignature(f->arguments().constFirst().type());
+ }
+ } else if (f->isConversionOperator()) {
signatures << f->ownerClass()->fullName();
+ }
}
const qsizetype size = signatures.size();
// PYSIDE-1328: `self`-ness cannot be computed in Python because there are mixed cases.
// Toplevel functions like `PySide6.QtCore.QEnum` are always self-less.
if (!(f->isStatic()) && f->ownerClass())
- args << u"self"_s;
+ args << PYTHON_SELF_VAR;
const auto &arguments = f->arguments();
for (qsizetype i = 0, size = arguments.size(); i < size; ++i) {
const auto n = i + 1;
QString returnType = f->pyiTypeReplaced(0); // pyi or modified type
if (returnType.isEmpty() && !f->isVoid())
- returnType = f->type().pythonSignature();
+ returnType = pythonSignature(f->type());
if (!returnType.isEmpty())
s << "->" << returnType;
etypeUsed |= writeEnumInitialization(s, cppEnum);
}
if (preambleWritten && !etypeUsed)
- s << sbkUnusedVariableCast(u"EType");
+ s << sbkUnusedVariableCast("EType");
}
static qsizetype maxLineLength(const QStringList &list)
bool etypeUsed = false;
- QString enumVarTypeObj = cpythonTypeNameExt(enumTypeEntry);
+ QString enumVarTypeObj = cpythonTypeNameExtSet(enumTypeEntry);
if (!cppEnum.isAnonymous()) {
int packageLevel = packageName().count(u'.') + 1;
s << "EType = Shiboken::Enum::"
if (cppEnum.typeEntry()->flags()) {
s << "// PYSIDE-1735: Mapping the flags class to the same enum class.\n"
- << cpythonTypeNameExt(cppEnum.typeEntry()->flags()) << " =\n"
+ << cpythonTypeNameExtSet(cppEnum.typeEntry()->flags()) << " =\n"
<< indent << "EType;\n" << outdent;
}
writeEnumConverterInitialization(s, cppEnum);
}
}
- s << "PySide::Signal::registerSignals(pyType, &::"
+ s << "PySide::Signal::registerSignals(pyType, &" << m_gsp
<< metaClass->qualifiedCppName() << "::staticMetaObject);\n";
}
return metaClass->qualifiedCppName();
}
+// Return the base type entries for introduceWrapperType()
+static ComplexTypeEntryCList pyBaseTypeEntries(const AbstractMetaClassCPtr &metaClass)
+{
+ ComplexTypeEntryCList result;
+ if (metaClass->isNamespace()) {
+ if (auto extended = metaClass->extendedNamespace())
+ result.append(extended->typeEntry());
+ return result;
+ }
+
+ const auto &baseClasses = metaClass->typeSystemBaseClasses();
+ for (auto base : baseClasses) {
+ for (; base != nullptr; base = base->baseClass()) { // Find a type that is not disabled.
+ const auto ct = base->typeEntry()->codeGeneration();
+ if (ct == TypeEntry::GenerateCode || ct == TypeEntry::GenerateForSubclass)
+ break;
+ }
+ result.append(base->typeEntry());
+ }
+ return result;
+}
+
+// Return the base type strings for introduceWrapperType()
+QStringList CppGenerator::pyBaseTypes(const AbstractMetaClassCPtr &metaClass)
+{
+ const auto &baseEntries = pyBaseTypeEntries(metaClass);
+ QStringList result;
+ for (const auto &baseEntry : baseEntries)
+ result.append("reinterpret_cast<PyObject *>("_L1 + cpythonTypeNameExt(baseEntry) + u')');
+ if (result.isEmpty()) // no base classes -> SbkObjectType.
+ result.append(sbkObjectTypeF);
+ return result;
+}
+
+void CppGenerator::writeInitInheritance(TextStream &s) const
+{
+ s << "static void " << initInheritanceFunction << "()\n{\n" << indent
+ << "auto &bm = Shiboken::BindingManager::instance();\n"
+ << sbkUnusedVariableCast("bm");
+ for (const auto &cls : api().classes()){
+ auto te = cls->typeEntry();
+ if (shouldGenerate(te)) {
+ const auto &baseEntries = pyBaseTypeEntries(cls);
+ if (!baseEntries.isEmpty()) {
+ const QString childTypeInitStruct = typeInitStruct(cls->typeEntry());
+ for (const auto &baseEntry : baseEntries) {
+ s << "bm.addClassInheritance(&" << typeInitStruct(baseEntry) << ",\n"
+ << Pad(' ', 23) << '&' << childTypeInitStruct << ");\n";
+ }
+ }
+ }
+ }
+ s << outdent << "}\n\n";
+}
+
void CppGenerator::writeClassRegister(TextStream &s,
const AbstractMetaClassCPtr &metaClass,
const GeneratorContext &classContext,
// PYSIDE-510: Create a signatures string for the introspection feature.
writeSignatureStrings(s, signatures, initFunctionName, "functions");
- s << "void init_" << initFunctionName;
- s << "(PyObject *" << enclosingObjectVariable << ")\n{\n" << indent;
+ s << "PyTypeObject *init_" << initFunctionName
+ << "(PyObject *" << enclosingObjectVariable << ")\n{\n" << indent;
+
+ const QString globalTypeVarExpr = !classContext.forSmartPointer()
+ ? cpythonTypeNameExtSet(classTypeEntry)
+ : cpythonTypeNameExtSet(classContext.preciseType());
+ s << "if (" << globalTypeVarExpr << " != nullptr)\n" << indent
+ << "return " << globalTypeVarExpr << ";\n\n" << outdent;
// Multiple inheritance
QString pyTypeBasesVariable = chopType(pyTypeName) + u"_Type_bases"_s;
- const auto &baseClasses = metaClass->typeSystemBaseClasses();
- if (metaClass->baseClassNames().size() > 1) {
- s << "PyObject *" << pyTypeBasesVariable
- << " = PyTuple_Pack(" << baseClasses.size() << ',' << '\n' << indent;
- for (qsizetype i = 0, size = baseClasses.size(); i < size; ++i) {
- if (i)
- s << ",\n";
- s << "reinterpret_cast<PyObject *>("
- << cpythonTypeNameExt(baseClasses.at(i)->typeEntry()) << ')';
- }
- s << ");\n\n" << outdent;
+ const QStringList pyBases = pyBaseTypes(metaClass);
+ s << "Shiboken::AutoDecRef " << pyTypeBasesVariable << "(PyTuple_Pack("
+ << pyBases.size() << ",\n" << indent;
+ for (qsizetype i = 0, size = pyBases.size(); i < size; ++i) {
+ if (i)
+ s << ",\n";
+ s << pyBases.at(i);
}
+ s << "));\n\n" << outdent;
// Create type and insert it in the module or enclosing class.
const QString typePtr = u"_"_s + chopType(pyTypeName)
if (dtorClassName.isEmpty())
s << "nullptr,\n";
else
- s << "&Shiboken::callCppDestructor< ::" << dtorClassName << " >,\n";
-
- // 6:baseType: Find a type that is not disabled.
- auto base = metaClass->isNamespace()
- ? metaClass->extendedNamespace() : metaClass->baseClass();
- if (!metaClass->isNamespace()) {
- for (; base != nullptr; base = base->baseClass()) {
- const auto ct = base->typeEntry()->codeGeneration();
- if (ct == TypeEntry::GenerateCode || ct == TypeEntry::GenerateForSubclass)
- break;
- }
- }
- if (base) {
- s << cpythonTypeNameExt(base->typeEntry()) << ",\n";
- } else {
- s << "nullptr,\n";
- }
+ s << "&Shiboken::callCppDestructor< " << globalScopePrefix(classContext)
+ << dtorClassName << " >,\n";
// 7:baseTypes
- if (metaClass->baseClassNames().size() > 1)
- s << pyTypeBasesVariable << ',' << '\n';
- else
- s << "0,\n";
+ s << pyTypeBasesVariable << ".object()," << '\n';
// 8:wrapperflags
QByteArrayList wrapperFlags;
wrapperFlags.append(QByteArrayLiteral("Shiboken::ObjectType::WrapperFlags::InnerClass"));
if (metaClass->deleteInMainThread())
wrapperFlags.append(QByteArrayLiteral("Shiboken::ObjectType::WrapperFlags::DeleteInMainThread"));
+ if (classTypeEntry->isValue())
+ wrapperFlags.append("Shiboken::ObjectType::WrapperFlags::Value"_ba);
if (wrapperFlags.isEmpty())
s << '0';
else
if (usePySideExtensions() && !classContext.forSmartPointer())
s << "SbkObjectType_SetPropertyStrings(pyType, "
<< chopType(pyTypeName) << "_PropertyStrings);\n";
-
- if (!classContext.forSmartPointer())
- s << cpythonTypeNameExt(classTypeEntry) << " = pyType;\n\n";
- else
- s << cpythonTypeNameExt(classContext.preciseType()) << " = pyType;\n\n";
+ s << globalTypeVarExpr << " = pyType;\n\n";
// Register conversions for the type.
writeConverterRegister(s, metaClass, classContext);
if (usePySideExtensions() && isQObject(metaClass)) {
s << "Shiboken::ObjectType::setSubTypeInitHook(pyType, &PySide::initQObjectSubType);\n"
- << "PySide::initDynamicMetaObject(pyType, &::"
+ << "PySide::initDynamicMetaObject(pyType, &" << m_gsp
<< metaClass->qualifiedCppName() << "::staticMetaObject, sizeof("
<< (shouldGenerateCppWrapper(metaClass)
? wrapperName(metaClass) : getFullTypeName(metaClass))
<< "));\n";
}
- s << outdent << "}\n";
+ s << "\nreturn pyType;\n" << outdent << "}\n";
}
void CppGenerator::writeStaticFieldInitialization(TextStream &s,
const AbstractMetaClassCPtr &metaClass)
{
- s << "\nvoid " << getSimpleClassStaticFieldsInitFunctionName(metaClass)
- << "()\n{\n" << indent << "Shiboken::AutoDecRef dict(PepType_GetDict(reinterpret_cast<PyTypeObject *>("
- << cpythonTypeName(metaClass) << ")));\n";
+ // cpythonTypeName == "Sbk_QRhiShaderResourceBinding_Data_TypeF"
+ QString name = cpythonTypeName(metaClass);
+ const auto parts = QStringView{name}.split(u'_', Qt::SkipEmptyParts);
+ if (parts.size() < 4) {
+ s << "\nPyTypeObject *" << getSimpleClassStaticFieldsInitFunctionName(metaClass)
+ << "(PyObject *module)\n{\n" << indent
+ << "auto *obType = PyObject_GetAttrString(module, \"" << metaClass->name() << "\");\n"
+ << "auto *type = reinterpret_cast<PyTypeObject *>(obType);\n"
+ << "Shiboken::AutoDecRef dict(PepType_GetDict(type));\n";
+ } else {
+ s << "\nPyTypeObject *" << getSimpleClassStaticFieldsInitFunctionName(metaClass)
+ << "(PyObject *module)\n{\n" << indent
+ << "auto *obContainerType = PyObject_GetAttrString(module, \""
+ << parts.at(1) << "\");\n"
+ << "auto *obType = PyObject_GetAttrString(obContainerType, \""
+ << parts.at(2) << "\");\n"
+ << "auto *type = reinterpret_cast<PyTypeObject *>(obType);\n"
+ << "Shiboken::AutoDecRef dict(PepType_GetDict(type));\n";
+ }
for (const AbstractMetaField &field : metaClass->fields()) {
if (field.isStatic()) {
s << "PyDict_SetItemString(dict, \"" << field.name()
s << ");\n";
}
}
- s << '\n' << outdent << "}\n";
+ s << "return type;\n" << outdent << "}\n";
}
enum class QtRegisterMetaType
case QtRegisterMetaType::None:
break;
case QtRegisterMetaType::Pointer:
- s << "qRegisterMetaType< ::" << className << " *>();\n";
+ s << "qRegisterMetaType< " << m_gsp << className << " *>();\n";
break;
case QtRegisterMetaType::Value:
for (const QString &name : std::as_const(nameVariants))
- s << "qRegisterMetaType< ::" << className << " >(\"" << name << "\");\n";
+ s << "qRegisterMetaType< " << m_gsp << className << " >(\"" << name << "\");\n";
break;
}
for (const AbstractMetaEnum &metaEnum : metaClass->enums()) {
if (!metaEnum.isPrivate() && !metaEnum.isAnonymous()) {
for (const QString &name : std::as_const(nameVariants)) {
- s << "qRegisterMetaType< ::"
+ s << "qRegisterMetaType< " << m_gsp
<< metaEnum.typeEntry()->qualifiedCppName() << " >(\""
<< name << "::" << metaEnum.name() << "\");\n";
}
}
}
+void CppGenerator::replacePolymorphicIdPlaceHolders(const AbstractMetaClassCPtr &metaClass,
+ QString *id)
+{
+ if (id->contains("%1"_L1)) {
+ QString replacement = " reinterpret_cast< "_L1 + m_gsp + metaClass->qualifiedCppName()
+ + " *>(cptr)"_L1;
+ id->replace("%1"_L1, replacement);
+ }
+ if (id->contains("%B"_L1)) {
+ auto baseClass = metaClass;
+ while (!baseClass->typeEntry()->isPolymorphicBase() && baseClass->baseClass())
+ baseClass = baseClass->baseClass();
+ QString replacement = " reinterpret_cast< "_L1 + m_gsp + baseClass->qualifiedCppName()
+ + " *>(cptr)"_L1;
+ id->replace("%B"_L1, replacement);
+ }
+}
+
void CppGenerator::writeTypeDiscoveryFunction(TextStream &s,
const AbstractMetaClassCPtr &metaClass)
{
s << "static void *" << cpythonBaseName(metaClass)
<< "_typeDiscovery(void *cptr, PyTypeObject *instanceType)\n{\n" << indent
- << sbkUnusedVariableCast(u"cptr"_s)
- << sbkUnusedVariableCast(u"instanceType");
+ << sbkUnusedVariableCast("cptr")
+ << sbkUnusedVariableCast("instanceType");
if (!polymorphicExpr.isEmpty()) {
- polymorphicExpr = polymorphicExpr.replace(u"%1"_s,
- u" reinterpret_cast< ::"_s
- + metaClass->qualifiedCppName()
- + u" *>(cptr)"_s);
- s << " if (" << polymorphicExpr << ")\n" << indent
+ replacePolymorphicIdPlaceHolders(metaClass, &polymorphicExpr);
+ s << "if (" << polymorphicExpr << ")\n" << indent
<< "return cptr;\n" << outdent;
} else if (metaClass->isPolymorphic()) {
const auto &ancestors = metaClass->allTypeSystemAncestors();
if (ancestor->baseClass())
continue;
if (ancestor->isPolymorphic()) {
- s << "if (instanceType == Shiboken::SbkType< ::"
+ s << "if (instanceType == Shiboken::SbkType< " << m_gsp
<< ancestor->qualifiedCppName() << " >())\n" << indent
<< "return dynamic_cast< " << getFullTypeName(metaClass)
<< " *>(reinterpret_cast< "<< getFullTypeName(ancestor)
if (attroCheck.testFlag(AttroCheckFlag::SetattroMethodOverride)
&& context.useWrapper()) {
s << "if (value != nullptr && PyCallable_Check(value) != 0) {\n" << indent
- << "auto plain_inst = " << cpythonWrapperCPtr(metaClass, u"self"_s) << ";\n"
+ << "auto plain_inst = " << cpythonWrapperCPtr(metaClass, PYTHON_SELF_VAR) << ";\n"
<< "auto *inst = dynamic_cast<" << context.wrapperName() << " *>(plain_inst);\n"
<< "if (inst != nullptr)\n" << indent
<< "inst->resetPyMethodCache();\n" << outdent << outdent
Q_ASSERT(func);
s << "{\n" << indent
<< "auto " << CPP_SELF_VAR << " = "
- << cpythonWrapperCPtr(metaClass, u"self"_s) << ";\n";
+ << cpythonWrapperCPtr(metaClass, PYTHON_SELF_VAR) << ";\n";
writeClassCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionAny,
TypeSystem::TargetLangCode, context);
s << outdent << "}\n";
{
static QString result;
if (result.isEmpty()) {
- auto qobjectClass = AbstractMetaClass::findClass(api().classes(), qObjectT());
+ auto qobjectClass = AbstractMetaClass::findClass(api().classes(), qObjectT);
Q_ASSERT(qobjectClass);
result = u"PySide::getHiddenDataFromQObject("_s
- + cpythonWrapperCPtr(qobjectClass, u"self"_s)
+ + cpythonWrapperCPtr(qobjectClass, PYTHON_SELF_VAR)
+ u", self, name)"_s;
}
return result;
<< "Shiboken::AutoDecRef tpDict(PepType_GetDict(Py_TYPE(self)));\n"
<< "if (auto *meth = PyDict_GetItem(tpDict.object(), tmp)) {\n" << indent;
// PYSIDE-1523: PyFunction_Check is not accepting compiled functions.
- s << "if (std::strcmp(Py_TYPE(meth)->tp_name, \"compiled_function\") == 0)\n" << indent
- << "return Py_TYPE(meth)->tp_descr_get(meth, self, nullptr);\n" << outdent
- << "return PyFunction_Check(meth) ? PyMethod_New(meth, self)\n"
+ s << "if (std::strcmp(Py_TYPE(meth)->tp_name, \"compiled_function\") == 0) {\n" << indent
+ << "auto descrGetFunc = "
+ << pyTypeGetSlot("descrgetfunc", "Py_TYPE(meth)", "Py_tp_descr_get") << ";\n"
+ << "return descrGetFunc(meth, self, nullptr);\n" << outdent
+ << "}\nreturn PyFunction_Check(meth) ? PyMethod_New(meth, self)\n"
<< " : " << getattrFunc << ";\n" << outdent
<< "}\n" << outdent << "}\n";
Q_ASSERT(func);
s << "{\n" << indent
<< "auto " << CPP_SELF_VAR << " = "
- << cpythonWrapperCPtr(metaClass, u"self"_s) << ";\n";
+ << cpythonWrapperCPtr(metaClass, PYTHON_SELF_VAR) << ";\n";
writeClassCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionAny,
TypeSystem::TargetLangCode, context);
s << outdent << "}\n";
// function.
void CppGenerator::writeInitFunc(TextStream &declStr, TextStream &callStr,
const QString &initFunctionName,
- const TypeEntryCPtr &enclosingEntry)
+ const TypeEntryCPtr &enclosingEntry,
+ const QString &pythonName, bool lazy)
{
- const bool hasParent =
- enclosingEntry && enclosingEntry->type() != TypeEntry::TypeSystemType;
- declStr << "void init_" << initFunctionName << "(PyObject *"
+ const QString functionName = "init_"_L1 + initFunctionName;
+ const bool hasParent = enclosingEntry && enclosingEntry->type() != TypeEntry::TypeSystemType;
+ declStr << "PyTypeObject *" << functionName << "(PyObject *"
<< (hasParent ? "enclosingClass" : "module") << ");\n";
- callStr << "init_" << initFunctionName;
- if (hasParent) {
- callStr << "(reinterpret_cast<PyObject *>("
- << cpythonTypeNameExt(enclosingEntry) << "));\n";
+
+ if (!lazy) {
+ const QString enclosing = hasParent
+ ? "reinterpret_cast<PyObject *>("_L1 + cpythonTypeNameExt(enclosingEntry) + u')'
+ : "module"_L1;
+ callStr << functionName << '(' << enclosing << ");\n";
+ } else if (hasParent) {
+ const QString &enclosingName = enclosingEntry->name();
+ const auto parts = QStringView{enclosingName}.split(u"::", Qt::SkipEmptyParts);
+ callStr << "Shiboken::Module::AddTypeCreationFunction("
+ << "module, \"" << pythonName << "\", " << functionName << ", \"";
+ for (qsizetype i = 0; i < parts.size(); ++i) {
+ if (i > 0)
+ callStr << "\", \"";
+ callStr << parts.at(i);
+ }
+ callStr << "\");\n";
} else {
- callStr << "(module);\n";
+ callStr << "Shiboken::Module::AddTypeCreationFunction("
+ << "module, \"" << pythonName << "\", "
+ << "init_" << initFunctionName << ");\n";
}
}
+static void writeSubModuleHandling(TextStream &s, const QString &moduleName,
+ const QString &subModuleOf)
+{
+ s << "{\n" << indent
+ << "Shiboken::AutoDecRef parentModule(Shiboken::Module::import(\""
+ << subModuleOf << "\"));\n"
+ << "if (parentModule.isNull())\n" << indent
+ << "return nullptr;\n" << outdent
+ << "if (PyModule_AddObject(parentModule.object(), \"" << moduleName
+ << "\", module) < 0)\n"
+ << indent << "return nullptr;\n" << outdent << outdent << "}\n";
+}
+
bool CppGenerator::finishGeneration()
{
//Generate CPython wrapper file
}
writeInitFunc(s_classInitDecl, s_classPythonDefines,
getSimpleClassInitFunctionName(cls),
- targetLangEnclosingEntry(te));
+ targetLangEnclosingEntry(te), cls->name());
if (cls->hasStaticFields()) {
- s_classInitDecl << "void "
- << getSimpleClassStaticFieldsInitFunctionName(cls) << "();\n";
+ s_classInitDecl << "PyTypeObject *"
+ << getSimpleClassStaticFieldsInitFunctionName(cls) << "(PyObject *module);\n";
classesWithStaticFields.append(cls);
}
if (hasConfigCondition) {
for (const auto &smp : api().instantiatedSmartPointers()) {
GeneratorContext context = contextForSmartPointer(smp.specialized, smp.type);
const auto enclosingClass = context.metaClass()->enclosingClass();
- auto enclosingTypeEntry = enclosingClass
- ? enclosingClass->typeEntry()
- : targetLangEnclosingEntry(smp.type.typeEntry());
+ auto enclosingTypeEntry = targetLangEnclosingEntry(smp.specialized->typeEntry());
writeInitFunc(s_classInitDecl, s_classPythonDefines,
getInitFunctionName(context),
- enclosingTypeEntry);
+ enclosingTypeEntry, smp.type.name());
includes.insert(smp.type.instantiations().constFirst().typeEntry()->include());
}
s << '\n';
}
+ // FIXME PYSIDE-7: Remove backwards compatible structure
s << "// Current module's type array.\n"
- << "PyTypeObject **" << cppApiVariableName() << " = nullptr;\n"
+ << "Shiboken::Module::TypeInitStruct *" << cppApiVariableName() << " = nullptr;\n"
+ << "// Backwards compatible structure with identical indexing.\n"
+ << "PyTypeObject **" << cppApiVariableNameOld() << " = nullptr;\n"
<< "// Current module's PyObject pointer.\n"
<< "PyObject *" << pythonModuleObjectName() << " = nullptr;\n"
<< "// Current module's converter array.\n"
- << "SbkConverter **" << convertersVariableName() << " = nullptr;\n";
+ << "SbkConverter **" << convertersVariableName() << " = nullptr;\n\n";
const CodeSnipList snips = moduleEntry->codeSnips();
// cleanup staticMetaObject attribute
if (usePySideExtensions()) {
+ QString iType = cppApiVariableName() + "[i].type"_L1;
+ QString iName = cppApiVariableName() + "[i].fullName"_L1;
+
s << "void cleanTypesAttributes() {\n" << indent
<< "static PyObject *attrName = Shiboken::PyName::qtStaticMetaObject();\n"
- << "for (int i = 0, imax = SBK_" << moduleName()
- << "_IDX_COUNT; i < imax; i++) {\n" << indent
- << "PyObject *pyType = reinterpret_cast<PyObject *>(" << cppApiVariableName() << "[i]);\n"
- << "if (pyType && PyObject_HasAttr(pyType, attrName))\n" << indent
+ << "const int imax = SBK_" << moduleName() << "_IDX_COUNT;\n"
+ << "for (int i = 0; i < imax && " << iName << " != nullptr; ++i) {\n" << indent
+ << "auto *pyType = reinterpret_cast<PyObject *>(" << iType << ");\n"
+ << "if (pyType != nullptr && PyObject_HasAttr(pyType, attrName))\n" << indent
<< "PyObject_SetAttr(pyType, attrName, Py_None);\n" << outdent
- << outdent << "}\n" << outdent << "}\n";
+ << outdent << "}\n" << outdent << "}\n\n";
}
s << "// Global functions "
if (!requiredModules.isEmpty())
s << "// Required modules' type and converter arrays.\n";
for (const QString &requiredModule : requiredModules) {
- s << "PyTypeObject **" << cppApiVariableName(requiredModule) << ";\n"
+ s << "Shiboken::Module::TypeInitStruct *" << cppApiVariableName(requiredModule) << ";\n"
<< "SbkConverter **" << convertersVariableName(requiredModule) << ";\n";
}
// PYSIDE-510: Create a signatures string for the introspection feature.
writeSignatureStrings(s, signatureStream.toString(), moduleName(), "global functions");
+ writeInitInheritance(s);
+
// Write module init function
const QString globalModuleVar = pythonModuleObjectName();
s << "extern \"C\" LIBSHIBOKEN_EXPORT PyObject *PyInit_"
int maxTypeIndex = getMaxTypeIndex() + api().instantiatedSmartPointers().size();
if (maxTypeIndex) {
- s << "// Create an array of wrapper types for the current module.\n"
- << "static PyTypeObject *cppApi[SBK_" << moduleName() << "_IDX_COUNT];\n"
- << cppApiVariableName() << " = cppApi;\n\n";
+ s << "// Create an array of wrapper types/names for the current module.\n"
+ << "static Shiboken::Module::TypeInitStruct cppApi[] = {\n" << indent;
+
+ // Windows did not like an array of QString.
+ QStringList typeNames;
+ for (int idx = 0; idx < maxTypeIndex; ++idx)
+ typeNames.append("+++ unknown entry #"_L1 + QString::number(idx)
+ + " in "_L1 + moduleName());
+
+ collectFullTypeNamesArray(typeNames);
+
+ for (auto typeName : typeNames)
+ s << "{nullptr, \"" << typeName << "\"},\n";
+
+ s << "{nullptr, nullptr}\n" << outdent << "};\n"
+ << "// The new global structure consisting of (type, name) pairs.\n"
+ << cppApiVariableName() << " = cppApi;\n";
+ if (usePySideExtensions())
+ s << "QT_WARNING_PUSH\nQT_WARNING_DISABLE_DEPRECATED\n";
+ s << "// The backward compatible alias with upper case indexes.\n"
+ << cppApiVariableNameOld() << " = reinterpret_cast<PyTypeObject **>(cppApi);\n";
+ if (usePySideExtensions())
+ s << "QT_WARNING_POP\n";
+ s << '\n';
}
s << "// Create an array of primitive type converters for the current module.\n"
<< "PyObject *module = Shiboken::Module::create(\"" << moduleName()
<< "\", &moduledef);\n\n"
<< "// Make module available from global scope\n"
- << globalModuleVar << " = module;\n\n"
- << "// Initialize classes in the type system\n"
+ << globalModuleVar << " = module;\n\n";
+
+ const QString subModuleOf = typeDb->defaultTypeSystemType()->subModuleOf();
+ if (!subModuleOf.isEmpty())
+ writeSubModuleHandling(s, moduleName(), subModuleOf);
+
+ s << "// Initialize classes in the type system\n"
<< s_classPythonDefines.toString();
if (!typeConversions.isEmpty()) {
if (!containers.isEmpty()) {
s << '\n';
for (const AbstractMetaType &container : containers) {
- const QString converterObj = writeContainerConverterInitialization(s, container);
+ const QString converterObj = writeContainerConverterInitialization(s, container, api());
const auto it = opaqueContainers.constFind(container);
if (it != opaqueContainers.constEnd()) {
writeSetPythonToCppPointerConversion(s, converterObj,
if (!pte->referencesType())
continue;
TypeEntryCPtr referencedType = basicReferencedTypeEntry(pte);
- QString converter = converterObject(referencedType);
- QStringList cppSignature = pte->qualifiedCppName().split(u"::"_s, Qt::SkipEmptyParts);
- while (!cppSignature.isEmpty()) {
- QString signature = cppSignature.join(u"::"_s);
- s << "Shiboken::Conversions::registerConverterName("
- << converter << ", \"" << signature << "\");\n";
- cppSignature.removeFirst();
- }
+ registerConverterInScopes(s, pte->qualifiedCppName(), converterObject(referencedType));
}
s << '\n';
s << "\n// Static field initialization\n";
for (const auto &cls : std::as_const(classesWithStaticFields)) {
ConfigurableScope configScope(s, cls->typeEntry());
- s << getSimpleClassStaticFieldsInitFunctionName(cls) << "();\n";
+ s << getSimpleClassStaticFieldsInitFunctionName(cls) << "(module);\n";
}
}
- s << "\nif (" << shibokenErrorsOccurred << ") {\n" << indent
+ s << '\n' << initInheritanceFunction << "();\n"
+ << "\nif (" << shibokenErrorsOccurred << ") {\n" << indent
<< "PyErr_Print();\n"
<< "Py_FatalError(\"can't initialize module " << moduleName() << "\");\n"
<< outdent << "}\n";
if (parentIndex == 0) {
parentVariable = PYTHON_RETURN_VAR;
} else if (parentIndex == -1) {
- parentVariable = u"self"_s;
+ parentVariable = PYTHON_SELF_VAR;
} else {
parentVariable = usePyArgs
? pythonArgsAt(parentIndex - 1) : PYTHON_ARG;
if (childIndex == 0) {
childVariable = PYTHON_RETURN_VAR;
} else if (childIndex == -1) {
- childVariable = u"self"_s;
+ childVariable = PYTHON_SELF_VAR;
} else {
childVariable = usePyArgs
? pythonArgsAt(childIndex - 1) : PYTHON_ARG;
bool finishGeneration() override;
private:
+ struct VirtualMethodReturn
+ {
+ QString statement;
+ bool needsReference = false;
+ };
+
+
void generateSmartPointerClass(TextStream &s, const GeneratorContext &classContext);
void generateIncludes(TextStream &s, const GeneratorContext &classContext,
const IncludeGroupList &includes = {},
const AbstractMetaClassCList &innerClasses = {}) const;
static void writeInitFunc(TextStream &declStr, TextStream &callStr,
const QString &initFunctionName,
- const TypeEntryCPtr &enclosingEntry = {});
+ const TypeEntryCPtr &enclosingEntry,
+ const QString &pythonName, bool lazy = true);
static void writeCacheResetNative(TextStream &s, const GeneratorContext &classContext);
void writeConstructorNative(TextStream &s, const GeneratorContext &classContext,
const AbstractMetaFunctionCPtr &func) const;
void writeVirtualMethodNative(TextStream &s,
const AbstractMetaFunctionCPtr &func,
int cacheIndex) const;
+ void writeVirtualMethodPythonOverride(TextStream &s,
+ const AbstractMetaFunctionCPtr &func,
+ const CodeSnipList &snips,
+ const VirtualMethodReturn &returnStatement) const;
+ void writeUserAddedPythonOverride(TextStream &s,
+ const AbstractMetaFunctionCPtr &func) const;
void writeVirtualMethodCppCall(TextStream &s, const AbstractMetaFunctionCPtr &func,
const QString &funcName, const QList<CodeSnip> &snips,
const AbstractMetaArgument *lastArg, const TypeEntryCPtr &retType,
const QString &returnStatement, bool hasGil) const;
- static QString virtualMethodReturn(TextStream &s, const ApiExtractorResult &api,
- const AbstractMetaFunctionCPtr &func,
- const FunctionModificationList &functionModifications);
+
+ static VirtualMethodReturn virtualMethodReturn(const ApiExtractorResult &api,
+ const AbstractMetaFunctionCPtr &func,
+ const FunctionModificationList &functionModifications);
void writeMetaObjectMethod(TextStream &s, const GeneratorContext &classContext) const;
static void writeMetaCast(TextStream &s, const GeneratorContext &classContext);
static void writeTypeCheck(TextStream& s, const std::shared_ptr<OverloadDataNode> &overloadData,
const QString &argumentName);
+ static void replacePolymorphicIdPlaceHolders(const AbstractMetaClassCPtr &metaClass,
+ QString *id);
static void writeTypeDiscoveryFunction(TextStream &s,
const AbstractMetaClassCPtr &metaClass);
static void writeSignatureStrings(TextStream &s, const QString &signatures,
const QString &arrayName,
const char *comment);
+ void writeInitInheritance(TextStream &s) const;
void writeClassRegister(TextStream &s,
const AbstractMetaClassCPtr &metaClass,
const GeneratorContext &classContext,
const QString &signatures) const;
+ static QStringList pyBaseTypes(const AbstractMetaClassCPtr &metaClass);
static QString destructorClassName(const AbstractMetaClassCPtr &metaClass,
const GeneratorContext &classContext);
static void writeStaticFieldInitialization(TextStream &s,
void writeSignatureInfo(TextStream &s, const OverloadData &overloads) const;
QString signatureParameter(const AbstractMetaArgument &arg) const;
+ QString pythonSignature(const AbstractMetaType &type) const;
/// Writes the implementation of all methods part of python sequence protocol
void writeSequenceMethods(TextStream &s,
const AbstractMetaClassCPtr &metaClass,
const CustomConversionPtr &customConversion);
static void writeEnumConverterInitialization(TextStream &s, const AbstractMetaEnum &metaEnum);
static QString writeContainerConverterInitialization(TextStream &s,
- const AbstractMetaType &type);
+ const AbstractMetaType &type,
+ const ApiExtractorResult &api);
void writeSmartPointerConverterInitialization(TextStream &s, const AbstractMetaType &ype) const;
+
+ static QString typeInitStruct(const TypeEntryCPtr &te);
static void writeExtendedConverterInitialization(TextStream &s,
const TypeEntryCPtr &externalType,
const AbstractMetaClassCList &conversions);
static bool hasBoolCast(const AbstractMetaClassCPtr &metaClass)
{ return boolCast(metaClass).has_value(); }
- std::optional<AbstractMetaType>
- findSmartPointerInstantiation(const SmartPointerTypeEntryCPtr &pointer,
- const TypeEntryCPtr &pointee) const;
void clearTpFuncs();
static QString chopType(QString s);
const AbstractMetaType &valueType) const
{
// Generate template specialization of value converter helper unless it is already there
- const QString pyArg = u"pyArg"_s;
- const QString cppArg = u"cppArg"_s;
-
const QString valueTypeName = valueType.cppSignature();
const QString checkFunction = cpythonCheckFunction(valueType);
s << "template <>\nstruct ShibokenContainerValueConverter<"
<< valueTypeName << ">\n{\n";
// Type check
- s << indent << "static bool checkValue(PyObject *" << pyArg << ")\n{\n"
+ s << indent << "static bool checkValue(PyObject *" << PYTHON_ARG << ")\n{\n"
<< indent << "return " << checkFunction;
if (!checkFunction.contains(u'('))
s << '(';
- s << pyArg << ");\n"
+ s << PYTHON_ARG << ");\n"
<< outdent << "}\n\n";
// C++ to Python
s << valueTypeName << ' ';
if (passByConstRef)
s << '&';
- s << cppArg << ")\n{\n" << indent << "return ";
- writeToPythonConversion(s, valueType, nullptr, cppArg);
+ s << CPP_ARG << ")\n{\n" << indent << "return ";
+ writeToPythonConversion(s, valueType, nullptr, CPP_ARG);
s << ";\n" << outdent << "}\n\n";
// Python to C++
s << "static std::optional<" << valueTypeName << "> convertValueToCpp(PyObject *"
- << pyArg << ")\n{\n" << indent;
+ << PYTHON_ARG << ")\n{\n" << indent;
s << PYTHON_TO_CPPCONVERSION_STRUCT << ' ' << PYTHON_TO_CPP_VAR << ";\n"
<< "if (!(";
- writeTypeCheck(s, valueType, pyArg), isNumber(valueType.typeEntry());
+ writeTypeCheck(s, valueType, PYTHON_ARG), isNumber(valueType.typeEntry());
s << ")) {\n" << indent
<< "Shiboken::Errors::setWrongContainerType();\n"
<< "return {};\n" << outdent << "}\n";
- writePythonToCppTypeConversion(s, valueType, pyArg, cppArg, nullptr, {});
- s << "return " << cppArg << ";\n" << outdent << "}\n" << outdent << "};\n\n";
+ writePythonToCppTypeConversion(s, valueType, PYTHON_ARG, CPP_ARG, nullptr, {});
+ s << "return " << CPP_ARG << ";\n" << outdent << "}\n" << outdent << "};\n\n";
}
// Generate code for a type wrapping a C++ container instantiation
// Check function
result.checkFunctionName = result.name + u"_Check"_s;
- const QString pyArg = u"pyArg"_s;
- s << "extern \"C\" int " << result.checkFunctionName << "(PyObject *" << pyArg
- << ")\n{\n" << indent << "return " << pyArg << " != nullptr && "
- << pyArg << " != Py_None && " << pyArg << "->ob_type == "
+ s << "extern \"C\" int " << result.checkFunctionName << "(PyObject *" << PYTHON_ARG
+ << ")\n{\n" << indent << "return " << PYTHON_ARG << " != nullptr && "
+ << PYTHON_ARG << " != Py_None && " << PYTHON_ARG << "->ob_type == "
<< typeFName << "();\n" << outdent << "}\n\n";
// SBK converter Python to C++
result.pythonToConverterFunctionName = u"PythonToCpp"_s + result.name;
s << "extern \"C\" void " << result.pythonToConverterFunctionName
- << "(PyObject *" << pyArg << ", void *cppOut)\n{\n" << indent
+ << "(PyObject *" << PYTHON_ARG << ", void *cppOut)\n{\n" << indent
<< "auto *d = ShibokenSequenceContainerPrivate<" << cppSignature
- << ">::get(" << pyArg << ");\n"
+ << ">::get(" << PYTHON_ARG << ");\n"
<< "*reinterpret_cast<" << cppSignature << "**>(cppOut) = d->m_list;\n"
<< outdent << "}\n\n";
// SBK check function for converting Python to C++ that returns the converter
result.converterCheckFunctionName = u"is"_s + result.name + u"PythonToCppConvertible"_s;
s << "extern \"C\" PythonToCppFunc " << result.converterCheckFunctionName
- << "(PyObject *" << pyArg << ")\n{\n" << indent << "if ("
- << result.checkFunctionName << '(' << pyArg << "))\n" << indent
+ << "(PyObject *" << PYTHON_ARG << ")\n{\n" << indent << "if ("
+ << result.checkFunctionName << '(' << PYTHON_ARG << "))\n" << indent
<< "return " << result.pythonToConverterFunctionName << ";\n"
<< outdent << "return {};\n" << outdent << "}\n\n";
return result;
}
-std::optional<AbstractMetaType>
- CppGenerator::findSmartPointerInstantiation(const SmartPointerTypeEntryCPtr &pointer,
- const TypeEntryCPtr &pointee) const
-{
- for (const auto &smp : api().instantiatedSmartPointers()) {
- const auto &i = smp.type;
- if (i.typeEntry() == pointer && i.instantiations().at(0).typeEntry() == pointee)
- return i;
- }
- return {};
-}
-
static bool hasParameterPredicate(const AbstractMetaFunctionCPtr &f)
{
return !f->arguments().isEmpty();
for (const auto &base : baseClasses) {
auto baseTe = base->typeEntry();
if (smartPointerTypeEntry->matchesInstantiation(baseTe)) {
- if (auto opt = findSmartPointerInstantiation(smartPointerTypeEntry, baseTe)) {
- const auto smartTargetType = opt.value();
+ if (auto opt = api().findSmartPointerInstantiation(smartPointerTypeEntry, baseTe)) {
+ const auto &smartTargetType = opt.value().type;
s << "// SmartPointer derived class: "
<< smartTargetType.cppSignature() << "\n";
writePythonToCppConversionFunctions(s, smartPointerType,
for (const auto &base : classes) {
auto baseTe = base->typeEntry();
- if (auto opt = findSmartPointerInstantiation(smartPointerTypeEntry, baseTe)) {
- const auto smartTargetType = opt.value();
+ if (auto opt = api().findSmartPointerInstantiation(smartPointerTypeEntry, baseTe)) {
+ const auto &smartTargetType = opt.value().type;
s << "// Convert to SmartPointer derived class: ["
<< smartTargetType.cppSignature() << "]\n";
const QString converter = u"Shiboken::Conversions::getConverter(\""_s
#include <QtCore/QString>
-static inline QString boolT() { return QStringLiteral("bool"); }
-static inline QString intT() { return QStringLiteral("int"); }
-static inline QString unsignedT() { return QStringLiteral("unsigned"); }
-static inline QString unsignedIntT() { return QStringLiteral("unsigned int"); }
-static inline QString longT() { return QStringLiteral("long"); }
-static inline QString unsignedLongT() { return QStringLiteral("unsigned long"); }
-static inline QString shortT() { return QStringLiteral("short"); }
-static inline QString unsignedShortT() { return QStringLiteral("unsigned short"); }
-static inline QString unsignedCharT() { return QStringLiteral("unsigned char"); }
-static inline QString longLongT() { return QStringLiteral("long long"); }
-static inline QString unsignedLongLongT() { return QStringLiteral("unsigned long long"); }
-static inline QString charT() { return QStringLiteral("char"); }
-static inline QString floatT() { return QStringLiteral("float"); }
-static inline QString doubleT() { return QStringLiteral("double"); }
-static inline QString constCharPtrT() { return QStringLiteral("const char*"); }
+constexpr auto boolT = QLatin1StringView("bool");
+constexpr auto intT = QLatin1StringView("int");
+constexpr auto unsignedT = QLatin1StringView("unsigned");
+constexpr auto unsignedIntT = QLatin1StringView("unsigned int");
+constexpr auto longT = QLatin1StringView("long");
+constexpr auto unsignedLongT = QLatin1StringView("unsigned long");
+constexpr auto shortT = QLatin1StringView("short");
+constexpr auto unsignedShortT = QLatin1StringView("unsigned short");
+constexpr auto unsignedCharT = QLatin1StringView("unsigned char");
+constexpr auto longLongT = QLatin1StringView("long long");
+constexpr auto unsignedLongLongT = QLatin1StringView("unsigned long long");
+constexpr auto charT = QLatin1StringView("char");
+constexpr auto floatT = QLatin1StringView("float");
+constexpr auto doubleT = QLatin1StringView("double");
+constexpr auto constCharPtrT = QLatin1StringView("const char*");
-static inline QString qByteArrayT() { return QStringLiteral("QByteArray"); }
-static inline QString qMetaObjectT() { return QStringLiteral("QMetaObject"); }
-static inline QString qObjectT() { return QStringLiteral("QObject"); }
-static inline QString qStringT() { return QStringLiteral("QString"); }
-static inline QString qVariantT() { return QStringLiteral("QVariant"); }
+constexpr auto qByteArrayT = QLatin1StringView("QByteArray");
+constexpr auto qMetaObjectT = QLatin1StringView("QMetaObject");
+constexpr auto qObjectT = QLatin1StringView("QObject");
+constexpr auto qStringT = QLatin1StringView("QString");
+constexpr auto qVariantT = QLatin1StringView("QVariant");
#endif // CTYPENAMES_H
#include <QtCore/QString>
-QString CPP_ARG(int i);
+QString CPP_ARG_N(int i);
QString CPP_ARG_REMOVED(int i);
-extern const QString CPP_RETURN_VAR;
-extern const QString CPP_SELF_VAR;
-extern const QString NULL_PTR;
-extern const QString PYTHON_ARG;
-extern const QString PYTHON_ARGS;
-extern const QString PYTHON_OVERRIDE_VAR;
-extern const QString PYTHON_RETURN_VAR;
-extern const QString PYTHON_TO_CPP_VAR;
+constexpr auto CPP_RETURN_VAR = QLatin1StringView("cppResult");
+constexpr auto CPP_SELF_VAR = QLatin1StringView("cppSelf");
+constexpr auto CPP_ARG = QLatin1StringView("cppArg");
+constexpr auto NULL_PTR = QLatin1StringView("nullptr");
+constexpr auto PYTHON_ARG = QLatin1StringView("pyArg");
+constexpr auto PYTHON_ARGS = QLatin1StringView("pyArgs");
+constexpr auto PYTHON_OVERRIDE_VAR = QLatin1StringView("pyOverride");
+constexpr auto PYTHON_RETURN_VAR = QLatin1StringView("pyResult");
+constexpr auto PYTHON_SELF_VAR = QLatin1StringView("self");
+constexpr auto PYTHON_TO_CPP_VAR = QLatin1StringView("pythonToCpp");
-extern const QString CONV_RULE_OUT_VAR_SUFFIX;
-extern const QString BEGIN_ALLOW_THREADS;
-extern const QString END_ALLOW_THREADS;
+constexpr auto CONV_RULE_OUT_VAR_SUFFIX = QLatin1StringView("_out");
+constexpr auto BEGIN_ALLOW_THREADS
+ = QLatin1StringView("PyThreadState *_save = PyEval_SaveThread(); // Py_BEGIN_ALLOW_THREADS");
+constexpr auto END_ALLOW_THREADS
+ = QLatin1StringView("PyEval_RestoreThread(_save); // Py_END_ALLOW_THREADS");
-extern const QString REPR_FUNCTION;
+constexpr auto REPR_FUNCTION = QLatin1StringView("__repr__");
+
+constexpr auto CPP_ARG0 = QLatin1StringView("cppArg0");
-extern const QString CPP_ARG0;
extern const char *const METHOD_DEF_SENTINEL;
extern const char *const PYTHON_TO_CPPCONVERSION_STRUCT;
extern const char *const openTargetExternC;
using namespace Qt::StringLiterals;
+struct IndexValue
+{
+ QString name; // "SBK_..."
+ int value;
+ QString comment;
+};
+
+TextStream &operator<<(TextStream &s, const IndexValue &iv)
+{
+ s << " " << AlignedField(iv.name, 56) << " = " << iv.value << ',';
+ if (!iv.comment.isEmpty())
+ s << " // " << iv.comment;
+ s << '\n';
+ return s;
+}
+
// PYSIDE-504: Handling the "protected hack"
// The problem: Creating wrappers when the class has private destructors.
// You can see an example on Windows in qclipboard_wrapper.h and others.
s << licenseComment();
QString wrapperName = classContext.effectiveClassName();
- QString outerHeaderGuard = getFilteredCppSignatureString(wrapperName).toUpper();
+ QString outerHeaderGuard = getFilteredCppSignatureString(wrapperName);
// Header
s << "#ifndef SBK_" << outerHeaderGuard << "_H\n";
if (!avoidProtectedHack())
s << protectedHackDefine;
- //Includes
+ // Includes
s << metaClass->typeEntry()->include() << '\n';
for (auto &inst : metaClass->templateBaseClassInstantiations())
s << inst.typeEntry()->include();
TypeSystem::CodeSnipPositionDeclaration, TypeSystem::NativeCode,
classContext);
- if ((!avoidProtectedHack() || !metaClass->hasPrivateDestructor())
- && usePySideExtensions() && isQObject(metaClass)) {
- s << outdent << "public:\n" << indent <<
- R"(int qt_metacall(QMetaObject::Call call, int id, void **args) override;
+ if (shouldGenerateMetaObjectFunctions(metaClass)) {
+ s << R"(
+const ::QMetaObject * metaObject() const override;
+int qt_metacall(QMetaObject::Call call, int id, void **args) override;
void *qt_metacast(const char *_clname) override;
)";
}
s << "static void pysideInitQtMetaTypes();\n";
s << "void resetPyMethodCache();\n"
- << outdent << "private:\n" << indent
- << "mutable bool m_PyMethodCache[" << maxOverrides << "];\n"
+ << outdent << "private:\n" << indent;
+
+ if (!metaClass->userAddedPythonOverrides().isEmpty()) {
+ for (const auto &f : metaClass->userAddedPythonOverrides())
+ s << functionSignature(f, {}, {}, Generator::OriginalTypeDescription) << ";\n";
+ s << '\n';
+ }
+
+ s << "mutable bool m_PyMethodCache[" << maxOverrides << "];\n"
<< outdent << "};\n\n";
}
{
Q_ASSERT(!func->isConstructor() && !func->isOperatorOverload());
s << "inline ";
- if (func->isStatic())
- s << "static ";
s << functionSignature(func, {}, postfix, Generator::OriginalTypeDescription)
<< " { ";
if (!func->isVoid())
}
const bool isVirtual = generation.testFlag(FunctionGenerationFlag::VirtualMethod);
- if (isVirtual || generation.testFlag(FunctionGenerationFlag::QMetaObjectMethod)) {
+ if (isVirtual) {
s << functionSignature(func, {}, {}, Generator::OriginalTypeDescription)
<< " override;\n";
}
}
}
-static void _writeTypeIndexValue(TextStream &s, const QString &variableName,
- int typeIndex)
-{
- s << " " << AlignedField(variableName, 56) << " = " << typeIndex;
-}
-
-static inline void _writeTypeIndexValueLine(TextStream &s,
- const QString &variableName,
- int typeIndex)
-{
- _writeTypeIndexValue(s, variableName, typeIndex);
- s << ",\n";
-}
-
// Find equivalent typedefs "using Foo=QList<int>", "using Bar=QList<int>"
static AbstractMetaClassCPtr
findEquivalentTemplateTypedef(const AbstractMetaClassCList &haystack,
return nullptr;
}
-void HeaderGenerator::writeTypeIndexValueLine(TextStream &s, const ApiExtractorResult &api,
- const TypeEntryCPtr &typeEntry)
+void HeaderGenerator::collectTypeEntryTypeIndexes(const ApiExtractorResult &api,
+ const TypeEntryCPtr &typeEntry,
+ IndexValues *indexValues)
{
if (!typeEntry || !typeEntry->generateCode())
return;
- s.setFieldAlignment(QTextStream::AlignLeft);
const int typeIndex = typeEntry->sbkIndex();
- _writeTypeIndexValueLine(s, getTypeIndexVariableName(typeEntry), typeIndex);
+ indexValues->append({getTypeIndexVariableName(typeEntry), typeIndex, {}});
+
if (typeEntry->isComplex()) {
// For a typedef "using Foo=QList<int>", write a type index
// SBK_QLIST_INT besides SBK_FOO which is then matched by function
metaClass) == nullptr) {
const QString indexVariable =
getTypeAlternateTemplateIndexVariableName(metaClass);
- _writeTypeIndexValueLine(s, indexVariable, typeIndex);
+ indexValues->append({indexVariable, typeIndex, {}});
m_alternateTemplateIndexes.append(m_alternateTemplateIndexes);
}
}
if (typeEntry->isEnum()) {
auto ete = std::static_pointer_cast<const EnumTypeEntry>(typeEntry);
if (ete->flags())
- writeTypeIndexValueLine(s, api, ete->flags());
+ collectTypeEntryTypeIndexes(api, ete->flags(), indexValues);
}
}
-void HeaderGenerator::writeTypeIndexValueLines(TextStream &s, const ApiExtractorResult &api,
- const AbstractMetaClassCPtr &metaClass)
+void HeaderGenerator::collectClassTypeIndexes(const ApiExtractorResult &api,
+ const AbstractMetaClassCPtr &metaClass,
+ IndexValues *indexValues)
{
auto typeEntry = metaClass->typeEntry();
if (!typeEntry->generateCode())
// enum indices are required for invisible namespaces as well.
for (const AbstractMetaEnum &metaEnum : metaClass->enums()) {
if (!metaEnum.isPrivate())
- writeTypeIndexValueLine(s, api, metaEnum.typeEntry());
+ collectTypeEntryTypeIndexes(api, metaEnum.typeEntry(), indexValues);
}
if (NamespaceTypeEntry::isVisibleScope(typeEntry))
- writeTypeIndexValueLine(s, api, typeEntry);
+ collectTypeEntryTypeIndexes(api, typeEntry, indexValues);
}
// Format the typedefs for the typedef entries to be generated
{
NameSpaces nameSpaces;
+ s << '\n';
+ auto typeSystemEntry = TypeDatabase::instance()->defaultTypeSystemType();
+ if (!typeSystemEntry->namespaceBegin().isEmpty())
+ s << typeSystemEntry->namespaceBegin() << '\n';
+
for (const auto &c : classList) {
if (auto encl = c->enclosingClass()) {
Q_ASSERT(encl->isNamespace());
if (nsp.nameSpace->enclosingClass() == nullptr)
writeNamespaceForwardDeclarationRecursion(s, i, nameSpaces);
}
+
+ if (!typeSystemEntry->namespaceEnd().isEmpty())
+ s << typeSystemEntry->namespaceEnd() << '\n';
}
// Include parameters required for the module/private module header
QString typeFunctions;
};
-bool HeaderGenerator::finishGeneration()
+HeaderGenerator::IndexValues
+ HeaderGenerator::collectTypeIndexes(const AbstractMetaClassCList &classList)
{
- // Generate the main header for this module. This header should be included
- // by binding modules extending on top of this one.
- ModuleHeaderParameters parameters;
- ModuleHeaderParameters privateParameters;
- StringStream macrosStream(TextStream::Language::Cpp);
-
- const auto snips = TypeDatabase::instance()->defaultTypeSystemType()->codeSnips();
- writeModuleCodeSnips(macrosStream, snips, TypeSystem::CodeSnipPositionDeclaration,
- TypeSystem::TargetLangCode);
-
- macrosStream << "// Type indices\nenum : int {\n";
- auto classList = api().classes();
-
- std::sort(classList.begin(), classList.end(),
- [](const AbstractMetaClassCPtr &a, const AbstractMetaClassCPtr &b) {
- return a->typeEntry()->sbkIndex() < b->typeEntry()->sbkIndex();
- });
+ IndexValues result;
for (const auto &metaClass : classList)
- writeTypeIndexValueLines(macrosStream, api(), metaClass);
+ collectClassTypeIndexes(api(), metaClass, &result);
for (const AbstractMetaEnum &metaEnum : api().globalEnums())
- writeTypeIndexValueLine(macrosStream, api(), metaEnum.typeEntry());
+ collectTypeEntryTypeIndexes(api(), metaEnum.typeEntry(), &result);
// Write the smart pointer define indexes.
int smartPointerCountIndex = getMaxTypeIndex();
int smartPointerCount = 0;
for (const auto &smp : api().instantiatedSmartPointers()) {
QString indexName = getTypeIndexVariableName(smp.type);
- _writeTypeIndexValue(macrosStream, indexName, smartPointerCountIndex);
- macrosStream << ", // " << smp.type.cppSignature() << '\n';
+ result.append({indexName, smartPointerCountIndex, smp.type.cppSignature()});
// Add a the same value for const pointees (shared_ptr<const Foo>).
const auto ptrName = smp.type.typeEntry()->entryName();
const auto pos = indexName.indexOf(ptrName, 0, Qt::CaseInsensitive);
if (pos >= 0) {
- indexName.insert(pos + ptrName.size() + 1, u"CONST"_s);
- _writeTypeIndexValue(macrosStream, indexName, smartPointerCountIndex);
- macrosStream << ", // (const)\n";
+ indexName.insert(pos + ptrName.size() + 1, u"const"_s);
+ result.append({indexName, smartPointerCountIndex, "(const)"_L1});
}
++smartPointerCountIndex;
++smartPointerCount;
}
+ result.append({"SBK_"_L1 + moduleName() + "_IDX_COUNT"_L1,
+ getMaxTypeIndex() + smartPointerCount, {}});
+ return result;
+}
- _writeTypeIndexValue(macrosStream,
- u"SBK_"_s + moduleName() + u"_IDX_COUNT"_s,
- getMaxTypeIndex() + smartPointerCount);
- macrosStream << "\n};\n";
-
- macrosStream << "// This variable stores all Python types exported by this module.\n";
- macrosStream << "extern PyTypeObject **" << cppApiVariableName() << ";\n\n";
- macrosStream << "// This variable stores the Python module object exported by this module.\n";
- macrosStream << "extern PyObject *" << pythonModuleObjectName() << ";\n\n";
- macrosStream << "// This variable stores all type converters exported by this module.\n";
- macrosStream << "extern SbkConverter **" << convertersVariableName() << ";\n\n";
-
- // TODO-CONVERTER ------------------------------------------------------------------------------
- // Using a counter would not do, a fix must be made to APIExtractor's getTypeIndex().
- macrosStream << "// Converter indices\nenum : int {\n";
+HeaderGenerator::IndexValues HeaderGenerator::collectConverterIndexes() const
+{
+ IndexValues result;
const auto &primitives = primitiveTypes();
int pCount = 0;
for (const auto &ptype : primitives) {
- /* Note: do not generate indices for typedef'd primitive types
- * as they'll use the primitive type converters instead, so we
- * don't need to create any other.
- */
- if (!ptype->generateCode() || !ptype->customConversion())
- continue;
-
- _writeTypeIndexValueLine(macrosStream, getTypeIndexVariableName(ptype), pCount++);
+ // Note: do not generate indices for typedef'd primitive types as
+ // they'll use the primitive type converters instead, so we
+ // don't need to create any other.
+ if (ptype->generateCode() && ptype->customConversion() != nullptr)
+ result.append({getTypeIndexVariableName(ptype), pCount++, {}});
}
for (const AbstractMetaType &container : api().instantiatedContainers()) {
- _writeTypeIndexValue(macrosStream, getTypeIndexVariableName(container), pCount);
- macrosStream << ", // " << container.cppSignature() << '\n';
- pCount++;
+ result.append({getTypeIndexVariableName(container),
+ pCount++, container.cppSignature()});
}
// Because on win32 the compiler will not accept a zero length array.
if (pCount == 0)
pCount++;
- _writeTypeIndexValue(macrosStream, QStringLiteral("SBK_%1_CONVERTERS_IDX_COUNT")
- .arg(moduleName()), pCount);
- macrosStream << "\n};\n";
+ result.append({"SBK_"_L1 + moduleName() + "_CONVERTERS_IDX_COUNT"_L1,
+ pCount, {}});
+ return result;
+}
+
+// PYSIDE-2404: Write the enums in unchanged case for reuse in type imports.
+// For conpatibility, we create them in uppercase, too and with
+// doubled index for emulating the former type-only case.
+//
+// FIXME: Remove in PySide 7. (See the note in `parser.py`)
+//
+static IndexValue typeIndexUpper(struct IndexValue const &ti)
+{
+ QString modi = ti.name.toUpper();
+ if (modi == ti.name)
+ modi = u"// "_s + modi;
+ return {modi, ti.value * 2, ti.comment};
+}
+
+bool HeaderGenerator::finishGeneration()
+{
+ // Generate the main header for this module. This header should be included
+ // by binding modules extending on top of this one.
+ ModuleHeaderParameters parameters;
+ ModuleHeaderParameters privateParameters;
+ StringStream macrosStream(TextStream::Language::Cpp);
+
+ const auto snips = TypeDatabase::instance()->defaultTypeSystemType()->codeSnips();
+ writeModuleCodeSnips(macrosStream, snips, TypeSystem::CodeSnipPositionDeclaration,
+ TypeSystem::TargetLangCode);
+
+ auto classList = api().classes();
+
+ std::sort(classList.begin(), classList.end(),
+ [](const AbstractMetaClassCPtr &a, const AbstractMetaClassCPtr &b) {
+ return a->typeEntry()->sbkIndex() < b->typeEntry()->sbkIndex();
+ });
+
+ const auto typeIndexes = collectTypeIndexes(classList);
+
+ macrosStream << "\n// Type indices\nenum [[deprecated]] : int {\n";
+ for (const auto &ti : typeIndexes)
+ macrosStream << typeIndexUpper(ti);
+ macrosStream << "};\n";
+
+ macrosStream << "\n// Type indices\nenum : int {\n";
+ for (const auto &ti : typeIndexes)
+ macrosStream << ti;
+ macrosStream << "};\n\n";
+
+ // FIXME: Remove backwards compatible variable in PySide 7.
+ macrosStream << "// This variable stores all Python types exported by this module.\n";
+ macrosStream << "extern Shiboken::Module::TypeInitStruct *" << cppApiVariableName() << ";\n\n";
+ macrosStream << "// This variable stores all Python types exported by this module ";
+ macrosStream << "in a backwards compatible way with identical indexing.\n";
+ macrosStream << "[[deprecated]] extern PyTypeObject **" << cppApiVariableNameOld() << ";\n\n";
+ macrosStream << "// This variable stores the Python module object exported by this module.\n";
+ macrosStream << "extern PyObject *" << pythonModuleObjectName() << ";\n\n";
+ macrosStream << "// This variable stores all type converters exported by this module.\n";
+ macrosStream << "extern SbkConverter **" << convertersVariableName() << ";\n\n";
+
+ // TODO-CONVERTER ------------------------------------------------------------------------------
+ // Using a counter would not do, a fix must be made to APIExtractor's getTypeIndex().
+ const auto converterIndexes = collectConverterIndexes();
+ macrosStream << "// Converter indices\nenum [[deprecated]] : int {\n";
+ for (const auto &ci : converterIndexes)
+ macrosStream << typeIndexUpper(ci);
+ macrosStream << "};\n\n";
+
+ macrosStream << "// Converter indices\nenum : int {\n";
+ for (const auto &ci : converterIndexes)
+ macrosStream << ci;
+ macrosStream << "};\n";
formatTypeDefEntries(macrosStream);
if (!shouldGenerate(classType))
continue;
- //Includes
+ // Includes
const bool isPrivate = classType->isPrivate();
auto &par = isPrivate ? privateParameters : parameters;
const auto classInclude = classType->include();
}
s << "#include <sbkpython.h>\n";
+ s << "#include <sbkmodule.h>\n";
s << "#include <sbkconverter.h>\n";
QStringList requiredTargetImports = TypeDatabase::instance()->requiredTargetImports();
TextStream &ps = privateFile.stream;
ps.setLanguage(TextStream::Language::Cpp);
QString privateIncludeShield =
- publicIncludeShield.left(publicIncludeShield.size() - 2)
- + QStringLiteral("_P_H");
+ publicIncludeShield.left(publicIncludeShield.size() - 2) + "_P_H"_L1;
ps << licenseComment()<< "\n\n";
: cppEnum.qualifiedCppName();
const auto te = cppEnum.typeEntry();
ConfigurableScope configScope(s, te);
- s << "template<> inline PyTypeObject *SbkType< ::" << enumName << " >() ";
+ s << "template<> inline PyTypeObject *SbkType< " << m_gsp << enumName << " >() ";
s << "{ return " << cpythonTypeNameExt(te) << "; }\n";
const auto flag = cppEnum.typeEntry()->flags();
if (flag) {
- s << "template<> inline PyTypeObject *SbkType< ::" << flag->name() << " >() "
+ s << "template<> inline PyTypeObject *SbkType< " << m_gsp << flag->name() << " >() "
<< "{ return " << cpythonTypeNameExt(flag) << "; }\n";
}
}
{
s << "template<> inline PyTypeObject *SbkType< "
<< getFullTypeName(cppClass) << " >() "
- << "{ return reinterpret_cast<PyTypeObject *>("
- << cpythonTypeNameExt(cppClass->typeEntry()) << "); }\n";
+ << "{ return " << cpythonTypeNameExt(cppClass->typeEntry()) << "; }\n";
}
void HeaderGenerator::writeSbkTypeFunction(TextStream &s, const AbstractMetaType &metaType)
{
- s << "template<> inline PyTypeObject *SbkType< ::" << metaType.cppSignature() << " >() "
+ s << "template<> inline PyTypeObject *SbkType< "
+ << m_gsp << metaType.cppSignature() << " >() "
<< "{ return " << cpythonTypeNameExt(metaType) << "; }\n";
}
#include "include.h"
#include "modifications_typedefs.h"
+#include <QtCore/QList>
#include <QtCore/QSet>
+struct IndexValue;
class AbstractMetaFunction;
struct ModuleHeaderParameters;
private:
using InheritedOverloadSet = QSet<AbstractMetaFunctionCPtr>;
+ using IndexValues = QList<IndexValue>;
+
+ IndexValues collectTypeIndexes(const AbstractMetaClassCList &classList);
+ IndexValues collectConverterIndexes() const;
static void writeCopyCtor(TextStream &s, const AbstractMetaClassCPtr &metaClass);
void writeFunction(TextStream &s,
static void writeSbkTypeFunction(TextStream &s, const AbstractMetaEnum &cppEnum);
static void writeSbkTypeFunction(TextStream &s, const AbstractMetaClassCPtr &cppClass);
static void writeSbkTypeFunction(TextStream &s, const AbstractMetaType &metaType);
- void writeTypeIndexValueLine(TextStream &s, const ApiExtractorResult &api,
- const TypeEntryCPtr &typeEntry);
- void writeTypeIndexValueLines(TextStream &s, const ApiExtractorResult &api,
- const AbstractMetaClassCPtr &metaClass);
+ void collectTypeEntryTypeIndexes(const ApiExtractorResult &api,
+ const TypeEntryCPtr &typeEntry,
+ IndexValues *indexValues);
+ void collectClassTypeIndexes(const ApiExtractorResult &api,
+ const AbstractMetaClassCPtr &metaClass,
+ IndexValues *indexValues);
static void writeProtectedEnumSurrogate(TextStream &s, const AbstractMetaEnum &cppEnum);
void writeMemberFunctionWrapper(TextStream &s,
const AbstractMetaFunctionCPtr &func,
// Primitive types that are not int, long, short,
// char and their respective unsigned counterparts.
- static const QStringList nonIntegerPrimitives{floatT(), doubleT(), boolT()};
+ static const QStringList nonIntegerPrimitives{floatT, doubleT, boolT};
// Signed integer primitive types.
- static const QStringList signedIntegerPrimitives{intT(), shortT(), longT(), longLongT()};
+ static const QStringList signedIntegerPrimitives{intT, shortT, longT, longLongT};
// sort the children overloads
for (const auto &ov : std::as_const(m_children))
it.value().append(ov);
}
- if (!checkPyObject && typeName == cPyObjectT())
+ if (!checkPyObject && typeName == cPyObjectT)
checkPyObject = true;
- else if (!checkPySequence && typeName == cPySequenceT())
+ else if (!checkPySequence && typeName == cPySequenceT)
checkPySequence = true;
- else if (!checkPyBuffer && typeName == cPyBufferT())
+ else if (!checkPyBuffer && typeName == cPyBufferT)
checkPyBuffer = true;
- else if (!checkQVariant && typeName == qVariantT())
+ else if (!checkQVariant && typeName == qVariantT)
checkQVariant = true;
- else if (!checkQString && typeName == qStringT())
+ else if (!checkQString && typeName == qStringT)
checkQString = true;
for (const auto &instantiation : ov->argType().instantiations()) {
// Create the graph of type dependencies based on implicit conversions.
// All C++ primitive types, add any forgotten type AT THE END OF THIS LIST!
- static const QStringList primitiveTypes{intT(), unsignedIntT(), longT(), unsignedLongT(),
- shortT(), unsignedShortT(), boolT(), unsignedCharT(), charT(), floatT(),
- doubleT(), constCharPtrT()};
+ static const QStringList primitiveTypes{intT, unsignedIntT, longT, unsignedLongT,
+ shortT, unsignedShortT, boolT, unsignedCharT, charT, floatT,
+ doubleT, constCharPtrT};
QStringList foundPrimitiveTypeIds;
for (const auto &p : primitiveTypes) {
}
if (checkPySequence && checkPyObject)
- graph.addEdge(cPySequenceT(), cPyObjectT());
+ graph.addEdge(cPySequenceT, cPyObjectT);
QStringList classesWithIntegerImplicitConversion;
else
convertibleType = getTypeName(function->arguments().constFirst().type());
- if (convertibleType == intT() || convertibleType == unsignedIntT())
+ if (convertibleType == intT || convertibleType == unsignedIntT)
classesWithIntegerImplicitConversion << targetTypeEntryName;
if (!graph.hasNode(convertibleType))
if ((checkPySequence || checkPyObject || checkPyBuffer)
- && !targetTypeEntryName.contains(cPyObjectT())
- && !targetTypeEntryName.contains(cPyBufferT())
- && !targetTypeEntryName.contains(cPySequenceT())) {
+ && !targetTypeEntryName.contains(cPyObjectT)
+ && !targetTypeEntryName.contains(cPyBufferT)
+ && !targetTypeEntryName.contains(cPySequenceT)) {
if (checkPySequence) {
// PySequence will be checked after all more specific types, but before PyObject.
- graph.addEdge(targetTypeEntryName, cPySequenceT());
+ graph.addEdge(targetTypeEntryName, cPySequenceT);
} else if (checkPyBuffer) {
// PySequence will be checked after all more specific types, but before PyObject.
- graph.addEdge(targetTypeEntryName, cPyBufferT());
+ graph.addEdge(targetTypeEntryName, cPyBufferT);
} else {
// Add dependency on PyObject, so its check is the last one (too generic).
- graph.addEdge(targetTypeEntryName, cPyObjectT());
+ graph.addEdge(targetTypeEntryName, cPyObjectT);
}
- } else if (checkQVariant && targetTypeEntryName != qVariantT()) {
- if (!graph.containsEdge(qVariantT(), targetTypeEntryName)) // Avoid cyclic dependency.
- graph.addEdge(targetTypeEntryName, qVariantT());
+ } else if (checkQVariant && targetTypeEntryName != qVariantT) {
+ if (!graph.containsEdge(qVariantT, targetTypeEntryName)) // Avoid cyclic dependency.
+ graph.addEdge(targetTypeEntryName, qVariantT);
} else if (checkQString && ov->argType().isPointer()
- && targetTypeEntryName != qStringT()
- && targetTypeEntryName != qByteArrayT()
- && (!checkPyObject || targetTypeEntryName != cPyObjectT())) {
- if (!graph.containsEdge(qStringT(), targetTypeEntryName)) // Avoid cyclic dependency.
- graph.addEdge(targetTypeEntryName, qStringT());
+ && targetTypeEntryName != qStringT
+ && targetTypeEntryName != qByteArrayT
+ && (!checkPyObject || targetTypeEntryName != cPyObjectT)) {
+ if (!graph.containsEdge(qStringT, targetTypeEntryName)) // Avoid cyclic dependency.
+ graph.addEdge(targetTypeEntryName, qStringT);
}
if (targetType.isEnum()) {
}
// QByteArray args need to be checked after QString args
- if (graph.hasNode(qStringT()) && graph.hasNode(qByteArrayT()))
- graph.addEdge(qStringT(), qByteArrayT());
+ if (graph.hasNode(qStringT) && graph.hasNode(qByteArrayT))
+ graph.addEdge(qStringT, qByteArrayT);
static const Edge rangeOrder[] =
- {{doubleT(), floatT()},
- {longLongT(), longT()}, {longLongT(), intT()}, {intT(), shortT()},
- {unsignedLongLongT(), unsignedLongT()}, {unsignedLongLongT(), unsignedT()},
- {unsignedLongLongT(), unsignedIntT()}, {unsignedT(), unsignedShortT()}
+ {{doubleT, floatT},
+ {longLongT, longT}, {longLongT, intT}, {intT, shortT},
+ {unsignedLongLongT, unsignedLongT}, {unsignedLongLongT, unsignedT},
+ {unsignedLongLongT, unsignedIntT}, {unsignedT, unsignedShortT}
};
for (const auto &r : rangeOrder) {
if (graph.hasNode(r.first) && graph.hasNode(r.second))
#include <QtCore/QString>
-static inline QString pyBoolT() { return QStringLiteral("PyBool"); }
-static inline QString pyFloatT() { return QStringLiteral("PyFloat"); }
-static inline QString pyLongT() { return QStringLiteral("PyLong"); }
-static inline QString pyObjectT() { return QStringLiteral("object"); }
-static inline QString pyStrT() { return QStringLiteral("str"); }
+constexpr auto pyBoolT = QLatin1StringView ("PyBool");
+constexpr auto pyFloatT = QLatin1StringView ("PyFloat");
+constexpr auto pyLongT = QLatin1StringView ("PyLong");
+constexpr auto pyObjectT = QLatin1StringView ("object");
+constexpr auto pyStrT = QLatin1StringView ("str");
// PYSIDE-1499: A custom type determined by existence of an `__fspath__` attribute.
-static inline QString pyPathLikeT() { return QStringLiteral("PyPathLike"); }
+constexpr auto pyPathLikeT = QLatin1StringView ("PyPathLike");
-static inline QString cPyBufferT() { return QStringLiteral("PyBuffer"); }
-static inline QString cPyListT() { return QStringLiteral("PyList"); }
-static inline QString cPyObjectT() { return QStringLiteral("PyObject"); }
-static inline QString cPySequenceT() { return QStringLiteral("PySequence"); }
-static inline QString cPyTypeObjectT() { return QStringLiteral("PyTypeObject"); }
+constexpr auto cPyBufferT = QLatin1StringView ("PyBuffer");
+constexpr auto cPyListT = QLatin1StringView ("PyList");
+constexpr auto cPyObjectT = QLatin1StringView ("PyObject");
+constexpr auto cPySequenceT = QLatin1StringView ("PySequence");
+constexpr auto cPyTypeObjectT = QLatin1StringView ("PyTypeObject");
// numpy
-static inline QString cPyArrayObjectT() { return QStringLiteral("PyArrayObject"); }
+constexpr auto cPyArrayObjectT = QLatin1StringView ("PyArrayObject");
-static inline QString sbkCharT() { return QStringLiteral("SbkChar"); }
+constexpr auto sbkCharT = QLatin1StringView ("SbkChar");
#endif // PYTYPENAMES_H
using namespace Qt::StringLiterals;
-static const char PARENT_CTOR_HEURISTIC[] = "enable-parent-ctor-heuristic";
-static const char RETURN_VALUE_HEURISTIC[] = "enable-return-value-heuristic";
-static const char DISABLE_VERBOSE_ERROR_MESSAGES[] = "disable-verbose-error-messages";
-static const char USE_ISNULL_AS_NB_BOOL[] = "use-isnull-as-nb-bool";
+static constexpr auto PARENT_CTOR_HEURISTIC = "enable-parent-ctor-heuristic"_L1;
+static constexpr auto RETURN_VALUE_HEURISTIC = "enable-return-value-heuristic"_L1;
+static constexpr auto DISABLE_VERBOSE_ERROR_MESSAGES = "disable-verbose-error-messages"_L1;
+static constexpr auto USE_ISNULL_AS_NB_BOOL = "use-isnull-as-nb-bool"_L1;
// FIXME PYSIDE 7: Remove USE_ISNULL_AS_NB_NONZERO/USE_OPERATOR_BOOL_AS_NB_NONZERO
-static const char USE_ISNULL_AS_NB_NONZERO[] = "use-isnull-as-nb_nonzero";
-static const char USE_OPERATOR_BOOL_AS_NB_BOOL[] = "use-operator-bool-as-nb-bool";
-static const char USE_OPERATOR_BOOL_AS_NB_NONZERO[] = "use-operator-bool-as-nb-nonzero";
-static const char WRAPPER_DIAGNOSTICS[] = "wrapper-diagnostics";
-static const char NO_IMPLICIT_CONVERSIONS[] = "no-implicit-conversions";
-static const char LEAN_HEADERS[] = "lean-headers";
+static constexpr auto USE_ISNULL_AS_NB_NONZERO = "use-isnull-as-nb_nonzero"_L1;
+static constexpr auto USE_OPERATOR_BOOL_AS_NB_BOOL = "use-operator-bool-as-nb-bool"_L1;
+static constexpr auto USE_OPERATOR_BOOL_AS_NB_NONZERO = "use-operator-bool-as-nb-nonzero"_L1;
+static constexpr auto WRAPPER_DIAGNOSTICS = "wrapper-diagnostics"_L1;
+static constexpr auto NO_IMPLICIT_CONVERSIONS = "no-implicit-conversions"_L1;
+static constexpr auto LEAN_HEADERS = "lean-headers"_L1;
-QString CPP_ARG(int i)
+QString CPP_ARG_N(int i)
{
- return u"cppArg"_s + QString::number(i);
+ return CPP_ARG + QString::number(i);
}
-static const QString CPP_ARG_REMOVED_PREFIX = u"removed_cppArg"_s;
+constexpr auto CPP_ARG_REMOVED_PREFIX = "removed_cppArg"_L1;
QString CPP_ARG_REMOVED(int i)
{
return CPP_ARG_REMOVED_PREFIX + QString::number(i);
}
-const QString CPP_RETURN_VAR = u"cppResult"_s;
-const QString CPP_SELF_VAR = u"cppSelf"_s;
-const QString NULL_PTR = u"nullptr"_s;
-const QString PYTHON_ARG = u"pyArg"_s;
-const QString PYTHON_ARGS = u"pyArgs"_s;
-const QString PYTHON_OVERRIDE_VAR = u"pyOverride"_s;
-const QString PYTHON_RETURN_VAR = u"pyResult"_s;
-const QString PYTHON_TO_CPP_VAR = u"pythonToCpp"_s;
-
-const QString CONV_RULE_OUT_VAR_SUFFIX = u"_out"_s;
-const QString BEGIN_ALLOW_THREADS =
- u"PyThreadState *_save = PyEval_SaveThread(); // Py_BEGIN_ALLOW_THREADS"_s;
-const QString END_ALLOW_THREADS = u"PyEval_RestoreThread(_save); // Py_END_ALLOW_THREADS"_s;
-
-const QString REPR_FUNCTION = u"__repr__"_s;
-
-const QString CPP_ARG0 = u"cppArg0"_s;
const char *const METHOD_DEF_SENTINEL = "{nullptr, nullptr, 0, nullptr} // Sentinel\n";
const char *const PYTHON_TO_CPPCONVERSION_STRUCT = "Shiboken::Conversions::PythonToCppConversion";
static const QHash<QString, QString> &primitiveTypesCorrespondences()
{
static const QHash<QString, QString> result = {
- {u"bool"_s, pyBoolT()},
- {u"char"_s, sbkCharT()},
- {u"signed char"_s, sbkCharT()},
- {u"unsigned char"_s, sbkCharT()},
- {intT(), pyLongT()},
- {u"signed int"_s, pyLongT()},
- {u"uint"_s, pyLongT()},
- {u"unsigned int"_s, pyLongT()},
- {shortT(), pyLongT()},
- {u"ushort"_s, pyLongT()},
- {u"signed short"_s, pyLongT()},
- {u"signed short int"_s, pyLongT()},
- {unsignedShortT(), pyLongT()},
- {u"unsigned short int"_s, pyLongT()},
- {longT(), pyLongT()},
- {doubleT(), pyFloatT()},
- {floatT(), pyFloatT()},
- {u"unsigned long"_s, pyLongT()},
- {u"signed long"_s, pyLongT()},
- {u"ulong"_s, pyLongT()},
- {u"unsigned long int"_s, pyLongT()},
- {u"long long"_s, pyLongT()},
- {u"__int64"_s, pyLongT()},
- {u"unsigned long long"_s, pyLongT()},
- {u"unsigned __int64"_s, pyLongT()},
- {u"size_t"_s, pyLongT()}
+ {u"bool"_s, pyBoolT},
+ {u"char"_s, sbkCharT},
+ {u"signed char"_s, sbkCharT},
+ {u"unsigned char"_s, sbkCharT},
+ {intT, pyLongT},
+ {u"signed int"_s, pyLongT},
+ {u"uint"_s, pyLongT},
+ {u"unsigned int"_s, pyLongT},
+ {shortT, pyLongT},
+ {u"ushort"_s, pyLongT},
+ {u"signed short"_s, pyLongT},
+ {u"signed short int"_s, pyLongT},
+ {unsignedShortT, pyLongT},
+ {u"unsigned short int"_s, pyLongT},
+ {longT, pyLongT},
+ {doubleT, pyFloatT},
+ {floatT, pyFloatT},
+ {u"unsigned long"_s, pyLongT},
+ {u"signed long"_s, pyLongT},
+ {u"ulong"_s, pyLongT},
+ {u"unsigned long int"_s, pyLongT},
+ {u"long long"_s, pyLongT},
+ {u"__int64"_s, pyLongT},
+ {u"unsigned long long"_s, pyLongT},
+ {u"unsigned __int64"_s, pyLongT},
+ {u"size_t"_s, pyLongT}
};
return result;
}
static const QHash<QString, QChar> result = {
{u"char"_s, u'b'},
{u"unsigned char"_s, u'B'},
- {intT(), u'i'},
+ {intT, u'i'},
{u"unsigned int"_s, u'I'},
- {shortT(), u'h'},
- {unsignedShortT(), u'H'},
- {longT(), u'l'},
- {unsignedLongLongT(), u'k'},
- {longLongT(), u'L'},
+ {shortT, u'h'},
+ {unsignedShortT, u'H'},
+ {longT, u'l'},
+ {unsignedLongLongT, u'k'},
+ {longLongT, u'L'},
{u"__int64"_s, u'L'},
- {unsignedLongLongT(), u'K'},
+ {unsignedLongLongT, u'K'},
{u"unsigned __int64"_s, u'K'},
- {doubleT(), u'd'},
- {floatT(), u'f'},
+ {doubleT, u'd'},
+ {floatT, u'f'},
};
return result;
}
&& wrapper.testFlag(AbstractMetaClass::CppProtectedHackWrapper));
}
+bool ShibokenGenerator::shouldGenerateMetaObjectFunctions(const AbstractMetaClassCPtr &metaClass)
+{
+ return usePySideExtensions()
+ && (!avoidProtectedHack() || !metaClass->hasPrivateDestructor())
+ && !metaClass->typeEntry()->typeFlags()
+ .testFlag(ComplexTypeEntry::DisableQtMetaObjectFunctions)
+ && isQObject(metaClass);
+}
+
ShibokenGenerator::FunctionGeneration ShibokenGenerator::functionGeneration(
const AbstractMetaFunctionCPtr &func)
{
// Check on virtuals (including operators).
const bool isAbstract = func->isAbstract();
if (!(isAbstract || func->isVirtual())
- || func->attributes().testFlag(AbstractMetaFunction::FinalCppMethod)
+ || func->cppAttributes().testFlag(FunctionAttribute::Final)
|| func->isModifiedFinal()) {
return result;
}
fullClassName.prepend(enclosing->name() + u'.');
enclosing = enclosing->enclosingClass();
}
- fullClassName.prepend(packageName() + u'.');
+ fullClassName.prepend(metaClass->typeEntry()->targetLangPackage() + u'.');
return fullClassName;
}
QString ShibokenGenerator::cpythonGetterFunctionName(const QString &name,
const AbstractMetaClassCPtr &enclosingClass)
{
- return cpythonBaseName(enclosingClass) + QStringLiteral("_get_") + name;
+ return cpythonBaseName(enclosingClass) + "_get_"_L1 + name;
}
QString ShibokenGenerator::cpythonSetterFunctionName(const QString &name,
const AbstractMetaClassCPtr &enclosingClass)
{
- return cpythonBaseName(enclosingClass) + QStringLiteral("_set_") + name;
+ return cpythonBaseName(enclosingClass) + "_set_"_L1 + name;
}
QString ShibokenGenerator::cpythonGetterFunctionName(const AbstractMetaField &metaField)
default:
Q_ASSERT(false);
}
- return cPySequenceT();
+ return cPySequenceT;
}
QString ShibokenGenerator::cpythonBaseName(const TypeEntryCPtr &type)
const auto ctype = std::static_pointer_cast<const ContainerTypeEntry>(type);
baseName = containerCpythonBaseName(ctype);
} else {
- baseName = cPyObjectT();
+ baseName = cPyObjectT;
}
return baseName.replace(u"::"_s, u"_"_s);
}
return cpythonBaseName(type) + u"_TypeF()"_s;
}
-QString ShibokenGenerator::cpythonTypeNameExt(const TypeEntryCPtr &type)
-{
- return cppApiVariableName(type->targetLangPackage()) + u'['
- + getTypeIndexVariableName(type) + u']';
-}
-
QString ShibokenGenerator::converterObject(const AbstractMetaType &type)
{
if (type.isCString())
return u"Shiboken::Conversions::PrimitiveTypeConverter<void *>()"_s;
const AbstractMetaTypeList nestedArrayTypes = type.nestedArrayTypes();
if (!nestedArrayTypes.isEmpty() && nestedArrayTypes.constLast().isCppPrimitive()) {
- return QStringLiteral("Shiboken::Conversions::ArrayTypeConverter<")
+ return "Shiboken::Conversions::ArrayTypeConverter<"_L1
+ nestedArrayTypes.constLast().minimalSignature()
+ u">("_s + QString::number(nestedArrayTypes.size())
+ u')';
+ u'[' + getTypeIndexVariableName(type) + u']';
}
-QString ShibokenGenerator::cpythonTypeNameExt(const AbstractMetaType &type)
+QString ShibokenGenerator::cpythonTypeNameExtSet(const TypeEntryCPtr &type)
+{
+ return cppApiVariableName(type->targetLangPackage()) + u'['
+ + getTypeIndexVariableName(type) + "].type"_L1;
+}
+
+QString ShibokenGenerator::cpythonTypeNameExtSet(const AbstractMetaType &type)
{
return cppApiVariableName(type.typeEntry()->targetLangPackage()) + u'['
- + getTypeIndexVariableName(type) + u']';
+ + getTypeIndexVariableName(type) + "].type"_L1;
}
-static inline QString unknownOperator() { return QStringLiteral("__UNKNOWN_OPERATOR__"); }
+QString ShibokenGenerator::cpythonTypeNameExt(const TypeEntryCPtr &type)
+{
+ return "Shiboken::Module::get("_L1 + cppApiVariableName(type->targetLangPackage())
+ + u'[' + getTypeIndexVariableName(type) + "])"_L1;
+}
+
+QString ShibokenGenerator::cpythonTypeNameExt(const AbstractMetaType &type)
+{
+ return u"Shiboken::Module::get("_s + cppApiVariableName(type.typeEntry()->targetLangPackage())
+ + u'[' + getTypeIndexVariableName(type) + "])"_L1;
+}
QString ShibokenGenerator::fixedCppTypeName(const TargetToNativeConversion &toNative)
{
QString op = Generator::pythonOperatorFunctionName(func->originalName());
if (op.isEmpty()) {
qCWarning(lcShiboken).noquote().nospace() << msgUnknownOperator(func.get());
- return unknownOperator();
+ return "__UNKNOWN_OPERATOR__"_L1;
}
if (func->arguments().isEmpty()) {
if (op == u"__sub__")
bool ShibokenGenerator::isNumber(const QString &cpythonApiName)
{
- return cpythonApiName == pyFloatT() || cpythonApiName == pyLongT()
- || cpythonApiName == pyBoolT();
+ return cpythonApiName == pyFloatT || cpythonApiName == pyLongT
+ || cpythonApiName == pyBoolT;
}
static std::optional<TypeSystem::CPythonType>
if (!cPythonTypeOpt.has_value()) {
const auto &mapping = primitiveTypesCorrespondences();
const auto it = mapping.constFind(pte->name());
- return it != mapping.cend() && it.value() == pyLongT();
+ return it != mapping.cend() && it.value() == pyLongT;
}
return cPythonTypeOpt.value() == TypeSystem::CPythonType::Integer;
}
+ (type.isPointer() ? u"Pointer"_s : u"Copy"_s)
+ u'(' + cpythonTypeNameExt(type) + u", "_s;
}
- return QStringLiteral("Shiboken::Conversions::pythonToCppCopy(%1, ")
- .arg(converterObject(type));
+ return "Shiboken::Conversions::pythonToCppCopy("_L1
+ + converterObject(type) + ", "_L1;
}
QString ShibokenGenerator::cpythonToPythonConversionFunction(const AbstractMetaType &type)
Options options) const
{
int argUsed = 0;
+ if (func->isUserAddedPythonOverride()) {
+ s << "Shiboken::GilState &gil, PyObject *" << PYTHON_OVERRIDE_VAR;
+ argUsed += 2;
+ }
for (const auto &arg : func->arguments()) {
if (options.testFlag(Generator::SkipRemovedArguments) && arg.isModifiedRemoved())
continue;
{
StringStream s(TextStream::Language::Cpp);
// The actual function
+ if (!options.testFlag(Option::SkipDefaultValues) && func->isStatic()) // Declaration
+ s << "static ";
if (func->isEmptyFunction() || func->needsReturnType())
s << functionReturnType(func, options) << ' ';
else
} else {
argValue = hasConversionRule
? arg.name() + CONV_RULE_OUT_VAR_SUFFIX
- : CPP_ARG(argPos);
+ : CPP_ARG_N(argPos);
const auto generatorArg = GeneratorArgument::fromMetaType(type);
AbstractMetaType::applyDereference(&argValue, generatorArg.indirections);
}
static void replacePyArg0(TypeSystem::Language language, QString *code)
{
- static const QString pyArg0 = u"%PYARG_0"_s;
+ static constexpr auto pyArg0 = "%PYARG_0"_L1;
if (!code->contains(pyArg0))
return;
// Replace %PYARG_# variables.
replacePyArg0(language, &code);
- static const QRegularExpression pyArgsRegex(QStringLiteral("%PYARG_(\\d+)"));
+ static const QRegularExpression pyArgsRegex("%PYARG_(\\d+)"_L1);
Q_ASSERT(pyArgsRegex.isValid());
if (language == TypeSystem::TargetLangCode) {
if (usePyArgs) {
code.replace(pyArgsRegex, PYTHON_ARGS + u"[\\1-1]"_s);
} else {
- static const QRegularExpression pyArgsRegexCheck(QStringLiteral("%PYARG_([2-9]+)"));
+ static const QRegularExpression pyArgsRegexCheck("%PYARG_([2-9]+)"_L1);
Q_ASSERT(pyArgsRegexCheck.isValid());
const QRegularExpressionMatch match = pyArgsRegexCheck.match(code);
if (match.hasMatch()) {
} else {
// Replaces the simplest case of attribution to a
// Python argument on the binding virtual method.
- static const QRegularExpression pyArgsAttributionRegex(QStringLiteral("%PYARG_(\\d+)\\s*=[^=]\\s*([^;]+)"));
+ static const QRegularExpression pyArgsAttributionRegex("%PYARG_(\\d+)\\s*=[^=]\\s*([^;]+)"_L1);
Q_ASSERT(pyArgsAttributionRegex.isValid());
code.replace(pyArgsAttributionRegex, u"PyTuple_SET_ITEM("_s
+ PYTHON_ARGS + u".object(), \\1-1, \\2)"_s);
code.replace(argTypeVar, argTypeVal);
}
- static const QRegularExpression cppArgTypeRegexCheck(QStringLiteral("%ARG(\\d+)_TYPE"));
+ static const QRegularExpression cppArgTypeRegexCheck("%ARG(\\d+)_TYPE"_L1);
Q_ASSERT(cppArgTypeRegexCheck.isValid());
QRegularExpressionMatchIterator rit = cppArgTypeRegexCheck.globalMatch(code);
while (rit.hasNext()) {
if (isProtected) {
code.replace(u"%TYPE::%FUNCTION_NAME"_s,
- QStringLiteral("%1::%2_protected")
- .arg(wrapperName(func->ownerClass()), func->originalName()));
+ wrapperName(func->ownerClass()) + "::"_L1
+ + func->originalName() + "_protected"_L1);
code.replace(u"%FUNCTION_NAME"_s,
func->originalName() + u"_protected"_s);
}
// and false if it is a variable.
static bool isVariable(const QString &code)
{
- static const QRegularExpression expr(QStringLiteral("^\\s*\\*?\\s*[A-Za-z_][A-Za-z_0-9.]*\\s*(?:\\[[^\\[]+\\])*$"));
+ static const QRegularExpression expr("^\\s*\\*?\\s*[A-Za-z_][A-Za-z_0-9.]*\\s*(?:\\[[^\\[]+\\])*$"_L1);
Q_ASSERT(expr.isValid());
return expr.match(code.trimmed()).hasMatch();
}
case TypeSystemCheckFunction:
conversion = cpythonCheckFunction(conversionType);
if (conversionType.typeEntry()->isPrimitive()
- && (conversionType.typeEntry()->name() == cPyObjectT()
+ && (conversionType.typeEntry()->name() == cPyObjectT
|| !conversion.endsWith(u' '))) {
conversion += u'(';
break;
FunctionQueryOption::GetAttroFunction)) {
result |= AttroCheckFlag::GetattroUser;
}
- if (usePySideExtensions() && metaClass->qualifiedCppName() == qObjectT())
+ if (usePySideExtensions() && metaClass->qualifiedCppName() == qObjectT)
result |= AttroCheckFlag::SetattroQObject;
if (useOverrideCaching(metaClass))
result |= AttroCheckFlag::SetattroMethodOverride;
QString ShibokenGenerator::getModuleHeaderFileBaseName(const QString &moduleName)
{
- return moduleCppPrefix(moduleName).toLower() + QStringLiteral("_python");
+ return moduleCppPrefix(moduleName).toLower() + "_python"_L1;
}
QString ShibokenGenerator::getModuleHeaderFileName(const QString &moduleName)
{
- return getModuleHeaderFileBaseName(moduleName) + QStringLiteral(".h");
+ return getModuleHeaderFileBaseName(moduleName) + ".h"_L1;
}
QString ShibokenGenerator::getPrivateModuleHeaderFileName(const QString &moduleName)
{
- return getModuleHeaderFileBaseName(moduleName) + QStringLiteral("_p.h");
+ return getModuleHeaderFileBaseName(moduleName) + "_p.h"_L1;
}
IncludeGroupList ShibokenGenerator::classIncludes(const AbstractMetaClassCPtr &metaClass) const
static bool hidesBaseClassFunctions(const AbstractMetaFunctionCPtr &f)
{
- return 0 == (f->attributes()
- & (AbstractMetaFunction::OverriddenCppMethod | AbstractMetaFunction::FinalCppMethod));
+ auto attributes = f->cppAttributes();
+ return !attributes.testFlag(FunctionAttribute::Override)
+ && !attributes.testFlag(FunctionAttribute::Final);
}
void ShibokenGenerator::getInheritedOverloads(const AbstractMetaClassCPtr &scope,
QList<OptionDescription> ShibokenGenerator::options()
{
return {
- {QLatin1StringView(DISABLE_VERBOSE_ERROR_MESSAGES),
+ {DISABLE_VERBOSE_ERROR_MESSAGES,
u"Disable verbose error messages. Turn the python code hard to debug\n"
"but safe few kB on the generated bindings."_s},
- {QLatin1StringView(PARENT_CTOR_HEURISTIC),
+ {PARENT_CTOR_HEURISTIC,
u"Enable heuristics to detect parent relationship on constructors."_s},
- {QLatin1StringView(RETURN_VALUE_HEURISTIC),
+ {RETURN_VALUE_HEURISTIC,
u"Enable heuristics to detect parent relationship on return values\n"
"(USE WITH CAUTION!)"_s},
- {QLatin1StringView(USE_ISNULL_AS_NB_BOOL),
+ {USE_ISNULL_AS_NB_BOOL,
u"If a class have an isNull() const method, it will be used to compute\n"
"the value of boolean casts"_s},
- {QLatin1StringView(LEAN_HEADERS),
+ {LEAN_HEADERS,
u"Forward declare classes in module headers"_s},
- {QLatin1StringView(USE_OPERATOR_BOOL_AS_NB_BOOL),
+ {USE_OPERATOR_BOOL_AS_NB_BOOL,
u"If a class has an operator bool, it will be used to compute\n"
"the value of boolean casts"_s},
- {QLatin1StringView(NO_IMPLICIT_CONVERSIONS),
+ {NO_IMPLICIT_CONVERSIONS,
u"Do not generate implicit_conversions for function arguments."_s},
- {QLatin1StringView(WRAPPER_DIAGNOSTICS),
+ {WRAPPER_DIAGNOSTICS,
u"Generate diagnostic code around wrappers"_s}
};
}
{
if (source == OptionSource::CommandLineSingleDash)
return false;
- if (key == QLatin1StringView(PARENT_CTOR_HEURISTIC))
+ if (key == PARENT_CTOR_HEURISTIC)
return (m_options->useCtorHeuristic = true);
- if (key == QLatin1StringView(RETURN_VALUE_HEURISTIC))
+ if (key == RETURN_VALUE_HEURISTIC)
return (m_options->userReturnValueHeuristic = true);
- if (key == QLatin1StringView(DISABLE_VERBOSE_ERROR_MESSAGES))
+ if (key == DISABLE_VERBOSE_ERROR_MESSAGES)
return (m_options->verboseErrorMessagesDisabled = true);
- if (key == QLatin1StringView(USE_ISNULL_AS_NB_BOOL)
- || key == QLatin1StringView(USE_ISNULL_AS_NB_NONZERO)) {
+ if (key == USE_ISNULL_AS_NB_BOOL || key == USE_ISNULL_AS_NB_NONZERO) {
return (m_options->useIsNullAsNbBool = true);
}
- if (key == QLatin1StringView(LEAN_HEADERS))
+ if (key == LEAN_HEADERS)
return (m_options->leanHeaders= true);
- if (key == QLatin1StringView(USE_OPERATOR_BOOL_AS_NB_BOOL)
- || key == QLatin1StringView(USE_OPERATOR_BOOL_AS_NB_NONZERO)) {
+ if (key == USE_OPERATOR_BOOL_AS_NB_BOOL || key == USE_OPERATOR_BOOL_AS_NB_NONZERO) {
return (m_options->useOperatorBoolAsNbBool = true);
}
- if (key == QLatin1StringView(NO_IMPLICIT_CONVERSIONS)) {
+ if (key == NO_IMPLICIT_CONVERSIONS) {
m_options->generateImplicitConversions = false;
return true;
}
- if (key == QLatin1StringView(WRAPPER_DIAGNOSTICS))
+ if (key == WRAPPER_DIAGNOSTICS)
return (m_options->wrapperDiagnostics = true);
return false;
}
return result;
}
+QString ShibokenGenerator::cppApiVariableNameOld(const QString &moduleName)
+{
+ return "Sbk"_L1 + moduleCppPrefix(moduleName) + "Types"_L1;
+}
+
QString ShibokenGenerator::cppApiVariableName(const QString &moduleName)
{
- return u"Sbk"_s + moduleCppPrefix(moduleName) + u"Types"_s;
+ return "Sbk"_L1 + moduleCppPrefix(moduleName) + "TypeStructs"_L1;
}
QString ShibokenGenerator::pythonModuleObjectName(const QString &moduleName)
{
- return u"Sbk"_s + moduleCppPrefix(moduleName) + u"ModuleObject"_s;
+ return "Sbk"_L1 + moduleCppPrefix(moduleName) + "ModuleObject"_L1;
}
QString ShibokenGenerator::convertersVariableName(const QString &moduleName)
{
- QString result = cppApiVariableName(moduleName);
+ QString result = cppApiVariableNameOld(moduleName);
result.chop(1);
result.append(u"Converters"_s);
return result;
static QString processInstantiationsVariableName(const AbstractMetaType &type)
{
- QString res = u'_' + _fixedCppTypeName(type.typeEntry()->qualifiedCppName()).toUpper();
+ QString res = u'_' + _fixedCppTypeName(type.typeEntry()->qualifiedCppName());
for (const auto &instantiation : type.instantiations()) {
res += instantiation.isContainer()
? processInstantiationsVariableName(instantiation)
- : u'_' + _fixedCppTypeName(instantiation.cppSignature()).toUpper();
+ : u'_' + _fixedCppTypeName(instantiation.cppSignature());
}
return res;
}
{
if (!s->endsWith(u'_'))
s->append(u'_');
- s->append(QStringLiteral("IDX"));
+ s->append("IDX"_L1);
}
QString
const auto templateBaseClass = metaClass->templateBaseClass();
Q_ASSERT(templateBaseClass);
QString result = u"SBK_"_s
- + _fixedCppTypeName(templateBaseClass->typeEntry()->qualifiedCppName()).toUpper();
+ + _fixedCppTypeName(templateBaseClass->typeEntry()->qualifiedCppName());
for (const auto &instantiation : metaClass->templateBaseClassInstantiations())
result += processInstantiationsVariableName(instantiation);
appendIndexSuffix(&result);
const int dot = package.lastIndexOf(u'.');
result += QStringView{package}.right(package.size() - (dot + 1));
}
- result += _fixedCppTypeName(type->qualifiedCppName()).toUpper();
+ result += _fixedCppTypeName(type->qualifiedCppName());
appendIndexSuffix(&result);
return result;
}
{
QString result = u"SBK"_s;
if (type.typeEntry()->isContainer())
- result += u'_' + moduleName().toUpper();
+ result += u'_' + moduleName();
result += processInstantiationsVariableName(type);
appendIndexSuffix(&result);
return result;
}
+void collectfromTypeEntry(TypeEntryCPtr entry, QStringList &typeNames)
+{
+ if (entry->shouldGenerate()) {
+ typeNames[entry->sbkIndex()] = entry->qualifiedTargetLangName();
+ if (entry->isEnum()) {
+ auto ete = std::static_pointer_cast<const EnumTypeEntry>(entry);
+ if (ete->flags()) {
+ auto entry = ete->flags();
+ typeNames[entry->sbkIndex()] = entry->qualifiedTargetLangName();
+ }
+ }
+ }
+}
+
+void ShibokenGenerator::collectFullTypeNamesArray(QStringList &typeNames)
+{
+ for (const auto &metaClass : api().classes()) {
+ collectfromTypeEntry(metaClass->typeEntry(), typeNames);
+
+ for (const AbstractMetaEnum &metaEnum : metaClass->enums())
+ collectfromTypeEntry(metaEnum.typeEntry(), typeNames);
+
+ int smartPointerCountIndex = getMaxTypeIndex();
+ for (const auto &smp : api().instantiatedSmartPointers()) {
+ auto entry = smp.type.typeEntry();
+ typeNames[smartPointerCountIndex] = entry->qualifiedTargetLangName();
+ ++smartPointerCountIndex;
+ }
+ }
+ for (const AbstractMetaEnum &metaEnum : api().globalEnums())
+ collectfromTypeEntry(metaEnum.typeEntry(), typeNames);
+}
+
bool ShibokenGenerator::verboseErrorMessagesDisabled()
{
return m_options.verboseErrorMessagesDisabled;
/// instead of only a Python wrapper.
static bool shouldGenerateCppWrapper(const AbstractMetaClassCPtr &metaClass);
+ static bool shouldGenerateMetaObjectFunctions(const AbstractMetaClassCPtr &metaClass);
+
/// Returns which functions need to be generated into the wrapper class
static FunctionGeneration functionGeneration(const AbstractMetaFunctionCPtr &func);
static QString cpythonBaseName(const AbstractMetaType &type);
static QString cpythonTypeName(const AbstractMetaClassCPtr &metaClass);
static QString cpythonTypeName(const TypeEntryCPtr &type);
+ static QString cpythonTypeNameExtSet(const TypeEntryCPtr &type);
+ static QString cpythonTypeNameExtSet(const AbstractMetaType &type);
static QString cpythonTypeNameExt(const TypeEntryCPtr &type);
- static QString cpythonTypeNameExt(const AbstractMetaType &type) ;
+ static QString cpythonTypeNameExt(const AbstractMetaType &type);
static QString cpythonCheckFunction(TypeEntryCPtr type);
static QString cpythonCheckFunction(AbstractMetaType metaType);
static QString cpythonIsConvertibleFunction(const TypeEntryCPtr &type);
static QString cpythonSetterFunctionName(const QPropertySpec &property,
const AbstractMetaClassCPtr &metaClass);
static QString cpythonWrapperCPtr(const AbstractMetaClassCPtr &metaClass,
- const QString &argName = QStringLiteral("self"));
- static QString cpythonWrapperCPtr(const AbstractMetaType &metaType,
+ const QString &argName = QLatin1StringView("self"));
+ static QString cpythonWrapperCPtr(const AbstractMetaType &metaType,
const QString &argName);
static QString cpythonWrapperCPtr(const TypeEntryCPtr &type, const QString &argName);
static bool useOperatorBoolAsNbBool();
/// Generate implicit conversions of function arguments
static bool generateImplicitConversions();
+ static QString cppApiVariableNameOld(const QString &moduleName = {});
static QString cppApiVariableName(const QString &moduleName = QString());
static QString pythonModuleObjectName(const QString &moduleName = QString());
static QString convertersVariableName(const QString &moduleName = QString());
static QString getTypeIndexVariableName(TypeEntryCPtr type);
static QString getTypeIndexVariableName(const AbstractMetaType &type) ;
+ /// Collect all type names as an array for initializing the type/name struct.
+ void collectFullTypeNamesArray(QStringList &typeNames);
+
/// Returns true if the user don't want verbose error messages on the generated bindings.
static bool verboseErrorMessagesDisabled();
+++ /dev/null
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "cppgenerator.h"
-#include "headergenerator.h"
-
-EXPORT_GENERATOR_PLUGIN(new CppGenerator << new HeaderGenerator)
gilstate.cpp gilstate.h
helper.cpp helper.h
pep384impl.cpp pep384impl.h
+pyobjectholder.h
sbkarrayconverter.cpp sbkarrayconverter.h sbkarrayconverter_p.h
sbkcontainer.cpp sbkcontainer.h
sbkconverter.cpp sbkconverter.h sbkconverter_p.h
bindingmanager.h
gilstate.h
helper.h
+ pyobjectholder.h
sbkarrayconverter.h
sbkcontainer.h
sbkconverter.h
sbkpython.h
sbkwindows.h
pep384impl.h
+ pep384ext.h
voidptr.h
bufferprocs_py37.h
"${CMAKE_CURRENT_BINARY_DIR}/sbkversion.h"
#define AUTODECREF_H
#include "sbkpython.h"
-#include "basewrapper.h"
#include <utility>
/**
* AutoDecRef holds a PyObject pointer and decrement its reference counter when destroyed.
*/
-struct LIBSHIBOKEN_API AutoDecRef
+struct AutoDecRef
{
public:
AutoDecRef(const AutoDecRef &) = delete;
AutoDecRef(AutoDecRef &&o) noexcept : m_pyObj{std::exchange(o.m_pyObj, nullptr)} {}
AutoDecRef &operator=(const AutoDecRef &) = delete;
- AutoDecRef &operator=(AutoDecRef &&o)
+ AutoDecRef &operator=(AutoDecRef &&o) noexcept
{
m_pyObj = std::exchange(o.m_pyObj, nullptr);
return *this;
Py_XDECREF(m_pyObj);
}
- inline bool isNull() const { return m_pyObj == nullptr; }
+ [[nodiscard]] bool isNull() const { return m_pyObj == nullptr; }
/// Returns the pointer of the Python object being held.
- inline PyObject *object() { return m_pyObj; }
- inline operator PyObject *() { return m_pyObj; }
+ [[nodiscard]] PyObject *object() const { return m_pyObj; }
+ [[nodiscard]] operator PyObject *() const { return m_pyObj; }
#ifndef Py_LIMITED_API
[[deprecated]] inline operator PyTupleObject *()
{ return reinterpret_cast<PyTupleObject *>(m_pyObj); }
} // namespace Shiboken
#endif // AUTODECREF_H
-
#include "basewrapper_p.h"
#include "bindingmanager.h"
#include "helper.h"
+#include "pep384ext.h"
#include "sbkconverter.h"
#include "sbkenum.h"
#include "sbkerrors.h"
#include "sbkfeature_base.h"
+#include "sbkmodule.h"
#include "sbkstring.h"
#include "sbkstaticstrings.h"
#include "sbkstaticstrings_p.h"
#include "signature_p.h"
#include "voidptr.h"
+#include <string>
#include <iostream>
+#include <sstream>
#if defined(__APPLE__)
#include <dlfcn.h>
// This was not needed before Python 3.8 (Python issue 35810)
Py_DECREF(Py_TYPE(self));
}
- Py_TYPE(self)->tp_free(self);
+ PepExt_TypeCallFree(self);
}
static void SbkObjectType_tp_dealloc(PyTypeObject *pyType);
"1:Shiboken.ObjectType",
static_cast<int>(PyType_Type.tp_basicsize) + 1, // see above
0, // sizeof(PyMemberDef), not for PyPy without a __len__ defined
- Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_TYPE_SUBCLASS,
+ SbkObjectType_Type_slots,
+ };
+
+ PyType_Spec SbkObjectType_Type_spec_312 = {
+ "1:Shiboken.ObjectType",
+ -long(sizeof(SbkObjectTypePrivate)),
+ 0, // sizeof(PyMemberDef), not for PyPy without a __len__ defined
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_TYPE_SUBCLASS,
SbkObjectType_Type_slots,
};
- return SbkType_FromSpec(&SbkObjectType_Type_spec);
+ return SbkType_FromSpec(_PepRuntimeVersion() >= 0x030C00 ?
+ &SbkObjectType_Type_spec_312 :
+ &SbkObjectType_Type_spec);
}
PyTypeObject *SbkObjectType_TypeF(void)
if (sbkSelf->ob_dict)
Py_VISIT(sbkSelf->ob_dict);
-#if PY_VERSION_HEX >= 0x03090000
// This was not needed before Python 3.9 (Python issue 35810 and 40217)
Py_VISIT(Py_TYPE(self));
-#endif
return 0;
}
// But before 3.12 is the minimum version, we cannot use the new
// function, although we would need this for 3.12 :-D
// We do a special patching here that is triggered through Py_None.
- return SbkType_FromSpec_BMDWB(&SbkObject_Type_spec,
- Py_None, // bases, special flag!
- SbkObjectType_TypeF(),
- offsetof(SbkObject, ob_dict),
- offsetof(SbkObject, weakreflist),
- nullptr); // bufferprocs
+ auto *type = SbkType_FromSpec_BMDWB(&SbkObject_Type_spec,
+ Py_None, // bases, spectial flag!
+ SbkObjectType_TypeF(),
+ offsetof(SbkObject, ob_dict),
+ offsetof(SbkObject, weakreflist),
+ nullptr); // bufferprocs
+ return type;
}
PyTypeObject *SbkObject_TypeF(void)
for (int i=0, i_max=PyTuple_GET_SIZE(pyBases); i < i_max; i++) {
PyObject *baseType = PyTuple_GET_ITEM(pyBases, i);
- if (reinterpret_cast<PyTypeObject *>(baseType)->tp_new == SbkDummyNew) {
+ if (PepExt_Type_GetNewSlot(reinterpret_cast<PyTypeObject *>(baseType)) == SbkDummyNew) {
// PYSIDE-595: A base class does not allow inheritance.
return reinterpret_cast<PyTypeObject *>(SbkDummyNew(metatype, args, kwds));
}
bool SbkObjectType_Check(PyTypeObject *type)
{
- static auto *obMeta = reinterpret_cast<PyObject *>(SbkObjectType_TypeF());
- auto *obType = reinterpret_cast<PyObject *>(type);
- return obMeta == reinterpret_cast<PyObject *>(Py_TYPE(obType))
- || PyObject_IsInstance(obType, obMeta);
+ static auto *meta = SbkObjectType_TypeF();
+ return Py_TYPE(type) == meta || PyType_IsSubtype(Py_TYPE(type), meta);
}
} //extern "C"
void _initMainThreadId(); // helper.cpp
+static std::string msgFailedToInitializeType(const char *description)
+{
+ std::ostringstream stream;
+ stream << "[libshiboken] Failed to initialize " << description;
+ if (auto *error = PepErr_GetRaisedException()) {
+ if (auto *str = PyObject_Str(error))
+ stream << ": " << Shiboken::String::toCString(str);
+ Py_DECREF(error);
+ }
+ stream << '.';
+ return stream.str();
+}
+
namespace Conversions { void init(); }
void init()
//Init private data
Pep384_Init();
- if (PyType_Ready(SbkObjectType_TypeF()) < 0)
- Py_FatalError("[libshiboken] Failed to initialize Shiboken.BaseWrapperType metatype.");
+ auto *type = SbkObjectType_TypeF();
+ if (type == nullptr || PyType_Ready(type) < 0)
+ Py_FatalError(msgFailedToInitializeType("Shiboken.BaseWrapperType metatype").c_str());
- if (PyType_Ready(SbkObject_TypeF()) < 0)
- Py_FatalError("[libshiboken] Failed to initialize Shiboken.BaseWrapper type.");
+ type = SbkObject_TypeF();
+ if (type == nullptr || PyType_Ready(type) < 0)
+ Py_FatalError(msgFailedToInitializeType("Shiboken.BaseWrapper type").c_str());
VoidPtr::init();
const char *originalName,
PyType_Spec *typeSpec,
ObjectDestructor cppObjDtor,
- PyTypeObject *baseType,
- PyObject *baseTypes,
+ PyObject *bases,
unsigned wrapperFlags)
{
- auto *base = baseType ? baseType : SbkObject_TypeF();
- typeSpec->slots[0].pfunc = reinterpret_cast<void *>(base);
- auto *bases = baseTypes ? baseTypes : PyTuple_Pack(1, base);
+ assert(PySequence_Fast_GET_SIZE(bases) > 0);
+ typeSpec->slots[0].pfunc = PySequence_Fast_GET_ITEM(bases, 0);
auto *type = SbkType_FromSpecBasesMeta(typeSpec, bases, SbkObjectType_TypeF());
- for (int i = 0; i < PySequence_Fast_GET_SIZE(bases); ++i) {
- auto *st = reinterpret_cast<PyTypeObject *>(PySequence_Fast_GET_ITEM(bases, i));
- BindingManager::instance().addClassInheritance(st, type);
- }
-
auto sotp = PepType_SOTP(type);
if (wrapperFlags & DeleteInMainThread)
sotp->delete_in_main_thread = 1;
+ sotp->type_behaviour = (wrapperFlags & Value) != 0
+ ? BEHAVIOUR_VALUETYPE : BEHAVIOUR_OBJECTTYPE;
setOriginalName(type, originalName);
setDestructorFunction(type, cppObjDtor);
void setSubTypeInitHook(PyTypeObject *type, SubTypeInitHook func)
{
+ assert(SbkObjectType_Check(type));
PepType_SOTP(type)->subtype_init = func;
}
void *getTypeUserData(PyTypeObject *type)
{
+ assert(SbkObjectType_Check(type));
return PepType_SOTP(type)->user_data;
}
void setTypeUserData(PyTypeObject *type, void *userData, DeleteUserDataFunc d_func)
{
+ assert(SbkObjectType_Check(type));
auto *sotp = PepType_SOTP(type);
sotp->user_data = userData;
sotp->d_func = d_func;
{
// Try to find the exact type of cptr.
if (!isExactType) {
- if (PyTypeObject *exactType = ObjectType::typeForTypeName(typeName))
+ if (PyTypeObject *exactType = ObjectType::typeForTypeName(typeName)) {
instanceType = exactType;
- else
- instanceType = BindingManager::instance().resolveType(&cptr, instanceType);
+ } else {
+ auto resolved = BindingManager::instance().findDerivedType(cptr, instanceType);
+ if (resolved.first != nullptr) {
+ instanceType = resolved.first;
+ cptr = resolved.second;
+ }
+ }
}
bool shouldCreate = true;
}
delete self->d; // PYSIDE-205: always delete d.
Py_XDECREF(self->ob_dict);
- Py_TYPE(self)->tp_free(self);
+ PepExt_TypeCallFree(reinterpret_cast<PyObject *>(self));
}
void setTypeUserData(SbkObject *wrapper, void *userData, DeleteUserDataFunc d_func)
: std::vector<PyTypeObject *>(1, Py_TYPE(self));
}
+static bool isValueType(SbkObject *self)
+{
+ return PepType_SOTP(Py_TYPE(self))->type_behaviour == BEHAVIOUR_VALUETYPE;
+}
+
void _debugFormat(std::ostream &s, SbkObject *self)
{
assert(self);
s << " [validCppObject]";
if (d->cppObjectCreated)
s << " [wasCreatedByPython]";
+ s << (isValueType(self) ? " [value]" : " [object]");
+
if (d->parentInfo) {
if (auto *parent = d->parentInfo->parent)
s << ", parent=" << reinterpret_cast<PyObject *>(parent)->ob_type->tp_name
s << "hasOwnership...... " << bool(self->d->hasOwnership) << "\n"
"containsCppWrapper " << self->d->containsCppWrapper << "\n"
"validCppObject.... " << self->d->validCppObject << "\n"
- "wasCreatedByPython " << self->d->cppObjectCreated << "\n";
-
+ "wasCreatedByPython " << self->d->cppObjectCreated << "\n"
+ "value...... " << isValueType(self) << "\n"
+ "reference count... " << reinterpret_cast<PyObject *>(self)->ob_refcnt << '\n';
if (self->d->parentInfo && self->d->parentInfo->parent) {
s << "parent............ ";
/// PYSIDE-1019: Set the function to select the current feature.
/// Return value is the previous content.
using SelectableFeatureHook = void (*)(PyTypeObject *);
+using SelectableFeatureCallback = void (*)(bool);
LIBSHIBOKEN_API SelectableFeatureHook initSelectableFeature(SelectableFeatureHook func);
+LIBSHIBOKEN_API void setSelectableFeatureCallback(SelectableFeatureCallback func);
/// PYSIDE-1626: Enforcing a context switch without further action.
LIBSHIBOKEN_API void SbkObjectType_UpdateFeature(PyTypeObject *type);
LIBSHIBOKEN_API void setTypeDiscoveryFunctionV2(PyTypeObject *self, TypeDiscoveryFuncV2 func);
LIBSHIBOKEN_API void copyMultipleInheritance(PyTypeObject *self, PyTypeObject *other);
LIBSHIBOKEN_API void setMultipleInheritanceFunction(PyTypeObject *self, MultipleInheritanceInitFunction func);
-LIBSHIBOKEN_API MultipleInheritanceInitFunction getMultipleInheritanceFunction(PyTypeObject *self);
+LIBSHIBOKEN_API MultipleInheritanceInitFunction getMultipleInheritanceFunction(PyTypeObject *type);
LIBSHIBOKEN_API void setDestructorFunction(PyTypeObject *self, ObjectDestructor func);
enum WrapperFlags
{
InnerClass = 0x1,
- DeleteInMainThread = 0x2
+ DeleteInMainThread = 0x2,
+ Value = 0x4
};
/**
* \returns true if the initialization went fine, false otherwise.
*/
LIBSHIBOKEN_API PyTypeObject *introduceWrapperType(PyObject *enclosingObject,
- const char *typeName,
- const char *originalName,
- PyType_Spec *typeSpec,
- ObjectDestructor cppObjDtor,
- PyTypeObject *baseType,
- PyObject *baseTypes,
- unsigned wrapperFlags = 0);
+ const char *typeName,
+ const char *originalName,
+ PyType_Spec *typeSpec,
+ ObjectDestructor cppObjDtor,
+ PyObject *bases,
+ unsigned wrapperFlags = 0);
/**
* Set the subtype init hook for a type.
#include "bindingmanager.h"
#include "gilstate.h"
#include "helper.h"
+#include "sbkmodule.h"
#include "sbkstring.h"
#include "sbkstaticstrings.h"
#include "sbkfeature_base.h"
#include <cstddef>
#include <cstring>
#include <fstream>
+#include <iostream>
#include <mutex>
+#include <string_view>
#include <unordered_map>
+#include <unordered_set>
+
+// GraphNode for the dependency graph. It keeps a pointer to
+// the TypeInitStruct to be able to lazily create the type and hashes
+// by the full type name.
+struct GraphNode
+{
+ explicit GraphNode(Shiboken::Module::TypeInitStruct *i) : name(i->fullName), initStruct(i) {}
+ explicit GraphNode(const char *n) : name(n), initStruct(nullptr) {} // Only for searching
+
+ std::string_view name;
+ Shiboken::Module::TypeInitStruct *initStruct;
+
+ friend bool operator==(const GraphNode &n1, const GraphNode &n2) { return n1.name == n2.name; }
+ friend bool operator!=(const GraphNode &n1, const GraphNode &n2) { return n1.name != n2.name; }
+};
+
+template <>
+struct std::hash<GraphNode> {
+ size_t operator()(const GraphNode &n) const noexcept
+ {
+ return std::hash<std::string_view>{}(n.name);
+ }
+};
namespace Shiboken
{
using WrapperMap = std::unordered_map<const void *, SbkObject *>;
-class Graph
+template <class NodeType>
+class BaseGraph
{
public:
- using NodeList = std::vector<PyTypeObject *>;
- using Edges = std::unordered_map<PyTypeObject *, NodeList>;
+ using NodeList = std::vector<NodeType>;
+ using NodeSet = std::unordered_set<NodeType>;
+
+ using Edges = std::unordered_map<NodeType, NodeList>;
Edges m_edges;
- Graph() = default;
+ BaseGraph() = default;
- void addEdge(PyTypeObject *from, PyTypeObject *to)
+ void addEdge(NodeType from, NodeType to)
{
m_edges[from].push_back(to);
}
-#ifndef NDEBUG
- void dumpDotGraph() const
+ NodeSet nodeSet() const
{
- std::ofstream file("/tmp/shiboken_graph.dot");
-
- file << "digraph D {\n";
-
+ NodeSet result;
for (const auto &p : m_edges) {
- auto *node1 = p.first;
- const NodeList &nodeList = p.second;
- for (const PyTypeObject *o : nodeList) {
- auto *node2 = o;
- file << '"' << node2->tp_name << "\" -> \""
- << node1->tp_name << "\"\n";
- }
+ result.insert(p.first);
+ for (const auto node2 : p.second)
+ result.insert(node2);
}
- file << "}\n";
+ return result;
}
-#endif
+};
+
+class Graph : public BaseGraph<GraphNode>
+{
+public:
+ using TypeCptrPair = BindingManager::TypeCptrPair;
- PyTypeObject *identifyType(void **cptr, PyTypeObject *type, PyTypeObject *baseType) const
+ TypeCptrPair identifyType(void *cptr, PyTypeObject *type, PyTypeObject *baseType) const
{
- auto edgesIt = m_edges.find(type);
- if (edgesIt != m_edges.end()) {
- const NodeList &adjNodes = m_edges.find(type)->second;
- for (PyTypeObject *node : adjNodes) {
- PyTypeObject *newType = identifyType(cptr, node, baseType);
- if (newType)
- return newType;
- }
- }
- void *typeFound = nullptr;
- auto *sotp = PepType_SOTP(type);
- if (sotp->type_discovery)
- typeFound = sotp->type_discovery(*cptr, baseType);
- if (typeFound) {
- // This "typeFound != type" is needed for backwards compatibility with old modules using a newer version of
- // libshiboken because old versions of type_discovery function used to return a PyTypeObject *instead of
- // a possible variation of the C++ instance pointer (*cptr).
- if (typeFound != type)
- *cptr = typeFound;
- return type;
- }
- return nullptr;
+ return identifyType(cptr, GraphNode(type->tp_name), type, baseType);
}
-};
+ bool dumpTypeGraph(const char *fileName) const;
-#ifndef NDEBUG
-static void showWrapperMap(const WrapperMap &wrapperMap)
+private:
+ TypeCptrPair identifyType(void *cptr, const GraphNode &typeNode, PyTypeObject *type,
+ PyTypeObject *baseType) const;
+};
+
+Graph::TypeCptrPair Graph::identifyType(void *cptr,
+ const GraphNode &typeNode, PyTypeObject *type,
+ PyTypeObject *baseType) const
{
- if (Shiboken::pyVerbose() > 0) {
- fprintf(stderr, "-------------------------------\n");
- fprintf(stderr, "WrapperMap: %p (size: %d)\n", &wrapperMap, (int) wrapperMap.size());
- for (auto it = wrapperMap.begin(), end = wrapperMap.end(); it != end; ++it) {
- const SbkObject *sbkObj = it->second;
- fprintf(stderr, "key: %p, value: %p (%s, refcnt: %d)\n", it->first,
- static_cast<const void *>(sbkObj),
- (Py_TYPE(sbkObj))->tp_name,
- int(Py_REFCNT(reinterpret_cast<const PyObject *>(sbkObj))));
+ assert(typeNode.initStruct != nullptr || type != nullptr);
+ auto edgesIt = m_edges.find(typeNode);
+ if (edgesIt != m_edges.end()) {
+ const NodeList &adjNodes = edgesIt->second;
+ for (const auto &node : adjNodes) {
+ auto newType = identifyType(cptr, node, nullptr, baseType);
+ if (newType.first != nullptr)
+ return newType;
}
- fprintf(stderr, "-------------------------------\n");
}
+
+ if (type == nullptr) {
+ if (typeNode.initStruct->type == nullptr) // Layzily create type
+ type = Shiboken::Module::get(*typeNode.initStruct);
+ else
+ type = typeNode.initStruct->type;
+ }
+
+ auto *sotp = PepType_SOTP(type);
+ if (sotp->type_discovery != nullptr) {
+ if (void *derivedCPtr = sotp->type_discovery(cptr, baseType))
+ return {type, derivedCPtr};
+ }
+ return {nullptr, nullptr};
+}
+
+static void formatDotNode(std::string_view name, std::ostream &file)
+{
+ auto lastDot = name.rfind('.');
+ file << " \"" << name << "\" [ label=";
+ if (lastDot != std::string::npos) {
+ file << '"' << name.substr(lastDot + 1) << "\" tooltip=\""
+ << name.substr(0, lastDot) << '"';
+ } else {
+ file << '"' << name << '"';
+ }
+ file << " ]\n";
+}
+
+bool Graph::dumpTypeGraph(const char *fileName) const
+{
+ std::ofstream file(fileName);
+ if (!file.good())
+ return false;
+
+ file << "digraph D {\n";
+
+ // Define nodes with short names
+ for (const auto &node : nodeSet())
+ formatDotNode(node.name, file);
+
+ // Write edges
+ for (const auto &p : m_edges) {
+ const auto &node1 = p.first;
+ const NodeList &nodeList = p.second;
+ for (const auto &node2 : nodeList)
+ file << " \"" << node2.name << "\" -> \"" << node1.name << "\"\n";
+ }
+ file << "}\n";
+ return true;
}
-#endif
struct BindingManager::BindingManagerPrivate {
using DestructorEntries = std::vector<DestructorEntry>;
std::recursive_mutex wrapperMapLock;
Graph classHierarchy;
DestructorEntries deleteInMainThread;
- bool destroying;
-
- BindingManagerPrivate() : destroying(false) {}
bool releaseWrapper(void *cptr, SbkObject *wrapper, const int *bases = nullptr);
bool releaseWrapperHelper(void *cptr, SbkObject *wrapper);
debugRemoveFreeHook();
#endif
#ifndef NDEBUG
- showWrapperMap(m_d->wrapperMapper);
+ if (Shiboken::pyVerbose() > 0)
+ dumpWrapperMap();
#endif
/* Cleanup hanging references. We just invalidate them as when
* the BindingManager is being destroyed the interpreter is alredy
return nullptr;
}
-void BindingManager::addClassInheritance(PyTypeObject *parent, PyTypeObject *child)
+void BindingManager::addClassInheritance(Module::TypeInitStruct *parent,
+ Module::TypeInitStruct *child)
{
- m_d->classHierarchy.addEdge(parent, child);
+ m_d->classHierarchy.addEdge(GraphNode(parent), GraphNode(child));
}
+BindingManager::TypeCptrPair BindingManager::findDerivedType(void *cptr, PyTypeObject *type) const
+{
+ return m_d->classHierarchy.identifyType(cptr, type, type);
+}
+
+// FIXME PYSIDE7: remove, just for compatibility
PyTypeObject *BindingManager::resolveType(void **cptr, PyTypeObject *type)
{
- PyTypeObject *identifiedType = m_d->classHierarchy.identifyType(cptr, type, type);
- return identifiedType ? identifiedType : type;
+ auto result = findDerivedType(*cptr, type);
+ if (result.second != nullptr)
+ *cptr = result.second;
+ return result.first != nullptr ? result.first : type;
}
std::set<PyObject *> BindingManager::getAllPyObjects()
}
}
+bool BindingManager::dumpTypeGraph(const char *fileName) const
+{
+ return m_d->classHierarchy.dumpTypeGraph(fileName);
+}
+
+void BindingManager::dumpWrapperMap()
+{
+ const auto &wrapperMap = m_d->wrapperMapper;
+ std::cerr << "-------------------------------\n"
+ << "WrapperMap size: " << wrapperMap.size() << " Types: "
+ << m_d->classHierarchy.nodeSet().size() << '\n';
+ for (auto it = wrapperMap.begin(), end = wrapperMap.end(); it != end; ++it) {
+ const SbkObject *sbkObj = it->second;
+ std::cerr << "key: " << it->first << ", value: "
+ << static_cast<const void *>(sbkObj) << " ("
+ << (Py_TYPE(sbkObj))->tp_name << ", refcnt: "
+ << Py_REFCNT(reinterpret_cast<const PyObject *>(sbkObj)) << ")\n";
+ }
+ std::cerr << "-------------------------------\n";
+}
+
static bool isPythonType(PyTypeObject *type)
{
// This is a type which should be called by multiple inheritance.
using Shiboken::AutoDecRef;
static PyObject *const _init = String::createStaticString("__init__");
+ static PyObject *objectInit =
+ PyObject_GetAttr(reinterpret_cast<PyObject *>(&PyBaseObject_Type), _init);
// A native C++ self cannot have multiple inheritance.
if (!Object::isUserType(self))
if (subType == &PyBaseObject_Type)
return false;
AutoDecRef func(PyObject_GetAttr(obSubType, _init));
+ // PYSIDE-2654: If this has no implementation then we get object.__init__
+ // but that is the same case like above.
+ if (func == objectInit)
+ return false;
// PYSIDE-2294: We need to explicitly ignore positional args in a mixin class.
SBK_UNUSED(args);
AutoDecRef newArgs(PyTuple_New(1));
#define BINDINGMANAGER_H
#include "sbkpython.h"
-#include <set>
#include "shibokenmacros.h"
+#include <set>
+#include <utility>
+
struct SbkObject;
namespace Shiboken
{
+namespace Module {
+struct TypeInitStruct;
+}
+
struct DestructorEntry;
using ObjectVisitor = void (*)(SbkObject *, void *);
SbkObject *retrieveWrapper(const void *cptr);
PyObject *getOverride(const void *cptr, PyObject *nameCache[], const char *methodName);
- void addClassInheritance(PyTypeObject *parent, PyTypeObject *child);
+ void addClassInheritance(Module::TypeInitStruct *parent, Module::TypeInitStruct *child);
+ /// Try to find the correct type of cptr via type discovery knowing that it's at least
+ /// of type \p type. If a derived class is found, it returns a cptr cast to the type
+ /// (which may be different in case of multiple inheritance.
+ /// \param cptr a pointer to the instance of type \p type
+ /// \param type type of cptr
+ using TypeCptrPair = std::pair<PyTypeObject *, void *>;
+ TypeCptrPair findDerivedType(void *cptr, PyTypeObject *type) const;
+
/**
* Try to find the correct type of *cptr knowing that it's at least of type \p type.
* In case of multiple inheritance this function may change the contents of cptr.
* \param type type of *cptr
* \warning This function is slow, use it only as last resort.
*/
- PyTypeObject *resolveType(void **cptr, PyTypeObject *type);
+ [[deprecated]] PyTypeObject *resolveType(void **cptr, PyTypeObject *type);
std::set<PyObject *> getAllPyObjects();
*/
void visitAllPyObjects(ObjectVisitor visitor, void *data);
+ bool dumpTypeGraph(const char *fileName) const;
+ void dumpWrapperMap();
+
private:
~BindingManager();
BindingManager();
#include <iomanip>
#include <iostream>
+#include <climits>
#include <cstring>
#include <cstdarg>
#include <cctype>
return std::nullopt;
}
+static bool verbose = false;
+
static void formatTypeTuple(PyObject *t, const char *what, std::ostream &str);
static void formatPyTypeObject(const PyTypeObject *obj, std::ostream &str, bool verbose)
{
- if (obj) {
+ if (obj == nullptr) {
+ str << '0';
+ return;
+ }
+
+ str << '"' << obj->tp_name << '"';
+ if (verbose) {
bool immutableType = false;
- str << '"' << obj->tp_name << "\", 0x" << std::hex
- << obj->tp_flags << std::dec;
+ str << ", 0x" << std::hex << obj->tp_flags << std::dec;
if (obj->tp_flags & Py_TPFLAGS_HEAPTYPE)
str << " [heaptype]";
if (obj->tp_flags & Py_TPFLAGS_BASETYPE)
str << " [readying]";
if (obj->tp_flags & Py_TPFLAGS_METHOD_DESCRIPTOR)
str << " [method_descriptor]";
-#if PY_VERSION_HEX >= 0x03090000
# ifndef Py_LIMITED_API
if (obj->tp_flags & Py_TPFLAGS_HAVE_VECTORCALL)
str << " [vectorcall]";
str << " [sequence]";
# endif // !Py_LIMITED_API
# endif // 3.10
-#endif // 3.9
if (obj->tp_basicsize != 0)
str << ", basicsize=" << obj->tp_basicsize;
if (verbose) {
}
}
}
- } else {
- str << '0';
}
}
{
// Note: The below call create the PyCompactUnicodeObject.utf8 representation
str << '"' << _PepUnicode_AsString(obj) << '"';
+ if (!verbose)
+ return;
str << " (" << PyUnicode_GetLength(obj) << ')';
const auto kind = _PepUnicode_KIND(obj);
str << "False";
return;
}
- str << "refs=" << Py_REFCNT(obj) << ", ";
+ const auto refs = Py_REFCNT(obj);
+ if (refs == UINT_MAX) // _Py_IMMORTAL_REFCNT
+ str << "immortal, ";
+ else
+ str << "refs=" << refs << ", ";
if (PyType_Check(obj)) {
str << "type: ";
formatPyTypeObject(reinterpret_cast<PyTypeObject *>(obj), str, true);
}
formatPyTypeObject(obj->ob_type, str, false);
str << ", ";
- if (PyLong_Check(obj))
- str << PyLong_AsLong(obj);
+ if (PyLong_Check(obj)) {
+ const auto llv = PyLong_AsLongLong(obj);
+ if (PyErr_Occurred() != PyExc_OverflowError) {
+ str << llv;
+ } else {
+ PyErr_Clear();
+ str << "0x" << std::hex << PyLong_AsUnsignedLongLong(obj) << std::dec;
+ }
+ }
else if (PyFloat_Check(obj))
str << PyFloat_AsDouble(obj);
else if (PyUnicode_Check(obj))
return str;
}
+std::ios_base &debugVerbose(std::ios_base &s)
+{
+ verbose = true;
+ return s;
+}
+
+std::ios_base &debugBrief(std::ios_base &s)
+{
+ verbose = false;
+ return s;
+}
+
#ifdef _WIN32
// Converts a Unicode string to a string encoded in the Windows console's
// code page via wchar_t for use with argv (PYSIDE-1425).
LIBSHIBOKEN_API std::ostream &operator<<(std::ostream &str, const debugPyTypeObject &o);
LIBSHIBOKEN_API std::ostream &operator<<(std::ostream &str, const debugPyBuffer &b);
LIBSHIBOKEN_API std::ostream &operator<<(std::ostream &str, const debugPyArrayObject &b);
-
+LIBSHIBOKEN_API std::ios_base &debugVerbose(std::ios_base &s);
+LIBSHIBOKEN_API std::ios_base &debugBrief(std::ios_base &s);
} // namespace Shiboken
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef PEP384EXT_H
+#define PEP384EXT_H
+
+#include "pep384impl.h"
+
+/// Returns the allocator slot of the PyTypeObject.
+inline allocfunc PepExt_Type_GetAllocSlot(PyTypeObject *t)
+{
+ return reinterpret_cast<allocfunc>(PepType_GetSlot(t, Py_tp_alloc));
+}
+
+/// Invokes the allocator slot of the PyTypeObject.
+template <class Type>
+inline Type *PepExt_TypeCallAlloc(PyTypeObject *t, Py_ssize_t nitems)
+{
+ PyObject *result = PepExt_Type_GetAllocSlot(t)(t, nitems);
+ return reinterpret_cast<Type *>(result);
+}
+
+/// Returns the getattro slot of the PyTypeObject.
+inline getattrofunc PepExt_Type_GetGetAttroSlot(PyTypeObject *t)
+{
+ return reinterpret_cast<getattrofunc>(PepType_GetSlot(t, Py_tp_getattro));
+}
+
+/// Returns the setattro slot of the PyTypeObject.
+inline setattrofunc PepExt_Type_GetSetAttroSlot(PyTypeObject *t)
+{
+ return reinterpret_cast<setattrofunc>(PepType_GetSlot(t, Py_tp_setattro));
+}
+
+/// Returns the descr_get slot of the PyTypeObject.
+inline descrgetfunc PepExt_Type_GetDescrGetSlot(PyTypeObject *t)
+{
+ return reinterpret_cast<descrgetfunc>(PepType_GetSlot(t, Py_tp_descr_get));
+}
+
+/// Invokes the descr_get slot of the PyTypeObject.
+inline PyObject *PepExt_Type_CallDescrGet(PyObject *self, PyObject *obj, PyObject *type)
+{
+ return PepExt_Type_GetDescrGetSlot(Py_TYPE(self))(self, obj, type);
+}
+
+/// Returns the descr_set slot of the PyTypeObject.
+inline descrsetfunc PepExt_Type_GetDescrSetSlot(PyTypeObject *t)
+{
+ return reinterpret_cast<descrsetfunc>(PepType_GetSlot(t, Py_tp_descr_set));
+}
+
+/// Returns the call slot of the PyTypeObject.
+inline ternaryfunc PepExt_Type_GetCallSlot(PyTypeObject *t)
+{
+ return reinterpret_cast<ternaryfunc>(PepType_GetSlot(t, Py_tp_call));
+}
+
+/// Returns the new slot of the PyTypeObject.
+inline newfunc PepExt_Type_GetNewSlot(PyTypeObject *t)
+{
+ return reinterpret_cast<newfunc>(PepType_GetSlot(t, Py_tp_new));
+}
+
+/// Returns the init slot of the PyTypeObject.
+inline initproc PepExt_Type_GetInitSlot(PyTypeObject *t)
+{
+ return reinterpret_cast<initproc>(PepType_GetSlot(t, Py_tp_init));
+}
+
+/// Returns the free slot of the PyTypeObject.
+inline freefunc PepExt_Type_GetFreeSlot(PyTypeObject *t)
+{
+ return reinterpret_cast<freefunc>(PepType_GetSlot(t, Py_tp_free));
+}
+
+/// Invokes the free slot of the PyTypeObject.
+inline void PepExt_TypeCallFree(PyTypeObject *t, void *object)
+{
+ PepExt_Type_GetFreeSlot(t)(object);
+}
+
+/// Invokes the free slot of the PyTypeObject.
+inline void PepExt_TypeCallFree(PyObject *object)
+{
+ PepExt_Type_GetFreeSlot(Py_TYPE(object))(object);
+}
+
+#endif // PEP384EXT_H
static void
check_PyTypeObject_valid()
{
- auto *obtype = reinterpret_cast<PyObject *>(&PyType_Type);
- auto *probe_tp_base = reinterpret_cast<PyTypeObject *>(
- PyObject_GetAttr(obtype, Shiboken::PyMagicName::base()));
+ auto *typetype = &PyType_Type;
+ auto *obtype = reinterpret_cast<PyObject *>(typetype);
+ auto *probe_tp_base_obj = PyObject_GetAttr(obtype, Shiboken::PyMagicName::base());
+ auto *probe_tp_base = reinterpret_cast<PyTypeObject *>(probe_tp_base_obj);
auto *probe_tp_bases = PyObject_GetAttr(obtype, Shiboken::PyMagicName::bases());
- auto *check = reinterpret_cast<PyTypeObject *>(
- PyType_FromSpecWithBases(&typeprobe_spec, probe_tp_bases));
- auto *typetype = reinterpret_cast<PyTypeObject *>(obtype);
+ auto *checkObj = PyType_FromSpecWithBases(&typeprobe_spec, probe_tp_bases);
+ auto *check = reinterpret_cast<PyTypeObject *>(checkObj);
PyObject *w = PyObject_GetAttr(obtype, Shiboken::PyMagicName::weakrefoffset());
long probe_tp_weakrefoffset = PyLong_AsLong(w);
PyObject *d = PyObject_GetAttr(obtype, Shiboken::PyMagicName::dictoffset());
|| probe_tp_mro != typetype->tp_mro
|| Py_TPFLAGS_DEFAULT != (check->tp_flags & Py_TPFLAGS_DEFAULT))
Py_FatalError("The structure of type objects has changed!");
- Py_DECREF(check);
- Py_DECREF(probe_tp_base);
+ Py_DECREF(checkObj);
+ Py_DECREF(probe_tp_base_obj);
Py_DECREF(w);
Py_DECREF(d);
Py_DECREF(probe_tp_bases);
}
#endif // Py_LIMITED_API
+// Support for pyerrors.h
+
+#if defined(Py_LIMITED_API) || PY_VERSION_HEX < 0x030C0000
+// Emulate PyErr_GetRaisedException() using the deprecated PyErr_Fetch()/PyErr_Store()
+PyObject *PepErr_GetRaisedException()
+{
+ PyObject *type{};
+ PyObject *value{};
+ PyObject *traceback{};
+ PyErr_Fetch(&type, &value, &traceback);
+ Py_XINCREF(value);
+ PyErr_Restore(type, value, traceback);
+ return value;
+}
+#endif // Limited or < 3.12
+
/*****************************************************************************
*
* Support for code.h
static PyTypeObject *
getStaticMethodType(void)
{
- // this works for Python 3, only
- // "StaticMethodType = type(str.__dict__['maketrans'])\n";
static const char prog[] =
- "from xxsubtype import spamlist\n"
- "result = type(spamlist.__dict__['staticmeth'])\n";
+ "result = type(str.__dict__['maketrans'])\n";
return reinterpret_cast<PyTypeObject *>(PepRun_GetResult(prog));
}
*
*/
+///////////////////////////////////////////////////////////////////////
+//
+// PEP 697: Support for embedded type structures.
+//
+// According to `https://docs.python.org/3/c-api/object.html?highlight=pyobject_gettypedata#c.PyObject_GetTypeData`
+// the function `PyObject_GetTypeData` should belong to the Stable API
+// since version 3.12.0, but it does not. We use instead some copies
+// from Python source code.
+
+#if !defined(Py_LIMITED_API) && PY_VERSION_HEX >= 0x030C0000
+
+# define PepObject_GetTypeData PyObject_GetTypeData
+
+SbkObjectTypePrivate *PepType_SOTP(PyTypeObject *type)
+{
+ // PYSIDE-2676: Use the meta type explicitly.
+ // A derived type would fail the offset calculation.
+ static auto *meta = SbkObjectType_TypeF();
+ assert(SbkObjectType_Check(type));
+ auto *obType = reinterpret_cast<PyObject *>(type);
+ void *data = PyObject_GetTypeData(obType, meta);
+ return reinterpret_cast<SbkObjectTypePrivate *>(data);
+}
+
+void PepType_SOTP_delete(PyTypeObject * /*type*/)
+{
+}
+
+#else
+
+// The following comments are directly copied from Python 3.12
+//
+
+// Make sure we have maximum alignment, even if the current compiler
+// does not support max_align_t. Note that:
+// - Autoconf reports alignment of unknown types to 0.
+// - 'long double' has maximum alignment on *most* platforms,
+// looks like the best we can do for pre-C11 compilers.
+// - The value is tested, see test_alignof_max_align_t
+# if !defined(ALIGNOF_MAX_ALIGN_T) || ALIGNOF_MAX_ALIGN_T == 0
+# undef ALIGNOF_MAX_ALIGN_T
+# define ALIGNOF_MAX_ALIGN_T alignof(long double)
+# endif
+
+/* Align up to the nearest multiple of alignof(max_align_t)
+ * (like _Py_ALIGN_UP, but for a size rather than pointer)
+ */
+static Py_ssize_t _align_up(Py_ssize_t size)
+{
+ return (size + ALIGNOF_MAX_ALIGN_T - 1) & ~(ALIGNOF_MAX_ALIGN_T - 1);
+}
+
+static void *PepObject_GetTypeData(PyObject *obj, PyTypeObject *cls)
+{
+ assert(PyObject_TypeCheck(obj, cls));
+ return reinterpret_cast<char *>(obj) + _align_up(cls->tp_base->tp_basicsize);
+}
+//
+///////////////////////////////////////////////////////////////////////
+
/*
* PyTypeObject extender
*/
+
static std::unordered_map<PyTypeObject *, SbkObjectTypePrivate > SOTP_extender{};
static thread_local PyTypeObject *SOTP_key{};
static thread_local SbkObjectTypePrivate *SOTP_value{};
-SbkObjectTypePrivate *PepType_SOTP(PyTypeObject *sbkType)
+SbkObjectTypePrivate *PepType_SOTP(PyTypeObject *type)
{
- if (sbkType == SOTP_key)
+ static auto *meta = SbkObjectType_TypeF();
+ static bool use_312 = _PepRuntimeVersion() >= 0x030C00;
+ assert(SbkObjectType_Check(type));
+ if (use_312) {
+ auto *obType = reinterpret_cast<PyObject *>(type);
+ void *data = PepObject_GetTypeData(obType, meta);
+ return reinterpret_cast<SbkObjectTypePrivate *>(data);
+ }
+ if (type == SOTP_key)
return SOTP_value;
- auto it = SOTP_extender.find(sbkType);
+ auto it = SOTP_extender.find(type);
if (it == SOTP_extender.end()) {
- it = SOTP_extender.insert({sbkType, {}}).first;
+ it = SOTP_extender.insert({type, {}}).first;
memset(&it->second, 0, sizeof(SbkObjectTypePrivate));
}
- SOTP_key = sbkType;
+ SOTP_key = type;
SOTP_value = &it->second;
return SOTP_value;
}
-void PepType_SOTP_delete(PyTypeObject *sbkType)
+void PepType_SOTP_delete(PyTypeObject *type)
{
- SOTP_extender.erase(sbkType);
+ static bool use_312 = _PepRuntimeVersion() >= 0x030C00;
+ assert(SbkObjectType_Check(type));
+ if (use_312)
+ return;
+ SOTP_extender.erase(type);
SOTP_key = nullptr;
}
+#endif // !defined(Py_LIMITED_API) && PY_VERSION_HEX >= 0x030C0000
+
/*
* SbkEnumType extender
*/
SbkEnumTypePrivate *PepType_SETP(SbkEnumType *enumType)
{
+ // PYSIDE-2230: This makes no sense at all for Enum types.
if (enumType == SETP_key)
return SETP_value;
auto it = SETP_extender.find(enumType);
return 0;
}
+// Pre 3.10, PyType_GetSlot() would only work for heap types.
+// FIXME: PyType_GetSlot() can be used unconditionally when the
+// minimum limited API version is >= 3.10.
+void *PepType_GetSlot(PyTypeObject *type, int aSlot)
+{
+ static const bool is310 = _PepRuntimeVersion() >= 0x030A00;
+ if (is310 || (type->tp_flags & Py_TPFLAGS_HEAPTYPE) != 0)
+ return PyType_GetSlot(type, aSlot);
+
+ switch (aSlot) {
+ case Py_tp_alloc:
+ return reinterpret_cast<void *>(type->tp_alloc);
+ case Py_tp_getattro:
+ return reinterpret_cast<void *>(type->tp_getattro);
+ case Py_tp_setattro:
+ return reinterpret_cast<void *>(type->tp_setattro);
+ case Py_tp_descr_get:
+ return reinterpret_cast<void *>(type->tp_descr_get);
+ case Py_tp_descr_set:
+ return reinterpret_cast<void *>(type->tp_descr_set);
+ case Py_tp_call:
+ return reinterpret_cast<void *>(type->tp_call);
+ case Py_tp_new:
+ return reinterpret_cast<void *>(type->tp_new);
+ case Py_tp_init:
+ return reinterpret_cast<void *>(type->tp_init);
+ case Py_tp_free:
+ return reinterpret_cast<void *>(type->tp_free);
+ }
+ assert(false);
+ return nullptr;
+}
+
/***************************************************************************
*
* PYSIDE-535: The enum/flag error
#ifndef PEP384IMPL_H
#define PEP384IMPL_H
-// PYSIDE-1436: Adapt to Python 3.10
-#if PY_VERSION_HEX < 0x030900A4
-# define Py_SET_REFCNT(obj, refcnt) ((Py_REFCNT(obj) = (refcnt)), (void)0)
-#endif
-
extern "C"
{
const char *tp_name;
Py_ssize_t tp_basicsize;
void *X03; // Py_ssize_t tp_itemsize;
+#ifdef PEP384_INTERN
destructor tp_dealloc;
+#else
+ destructor X04;
+#endif
void *X05; // Py_ssize_t tp_vectorcall_offset;
void *X06; // getattrfunc tp_getattr;
void *X07; // setattrfunc tp_setattr;
void *X08; // PyAsyncMethods *tp_as_async;
+#ifdef PEP384_INTERN
reprfunc tp_repr;
+#else
+ reprfunc X09;
+#endif
void *X10; // PyNumberMethods *tp_as_number;
void *X11; // PySequenceMethods *tp_as_sequence;
void *X12; // PyMappingMethods *tp_as_mapping;
void *X13; // hashfunc tp_hash;
+#ifdef PEP384_INTERN
ternaryfunc tp_call;
- reprfunc tp_str;
+#else
+ ternaryfunc X14;
+#endif
+ reprfunc tp_str; // Only used for PEP384_INTERN and a shiboken test
getattrofunc tp_getattro;
setattrofunc tp_setattro;
void *X18; // PyBufferProcs *tp_as_buffer;
unsigned long tp_flags;
void *X20; // const char *tp_doc;
+#ifdef PEP384_INTERN
traverseproc tp_traverse;
inquiry tp_clear;
+#else
+ traverseproc X21;
+ inquiry X22;
+#endif
void *X23; // richcmpfunc tp_richcompare;
Py_ssize_t tp_weaklistoffset;
void *X25; // getiterfunc tp_iter;
+#ifdef PEP384_INTERN
iternextfunc tp_iternext;
+#else
+ iternextfunc X26;
+#endif
struct PyMethodDef *tp_methods;
struct PyMemberDef *tp_members;
struct PyGetSetDef *tp_getset;
struct _typeobject *tp_base;
#ifdef PEP384_INTERN
PyObject *tp_dict;
+ descrgetfunc tp_descr_get;
+ descrsetfunc tp_descr_set;
#else
void *X31;
+ descrgetfunc X32;
+ descrsetfunc X33;
#endif
- descrgetfunc tp_descr_get;
- descrsetfunc tp_descr_set;
Py_ssize_t tp_dictoffset;
+#ifdef PEP384_INTERN
initproc tp_init;
allocfunc tp_alloc;
+#else
+ initproc X39;
+ allocfunc X40;
+#endif
newfunc tp_new;
+#ifdef PEP384_INTERN
freefunc tp_free;
inquiry tp_is_gc; /* For PyObject_IS_GC */
+#else
+ freefunc X41;
+ inquiry X42; /* For PyObject_IS_GC */
+#endif
PyObject *tp_bases;
PyObject *tp_mro; /* method resolution order */
LIBSHIBOKEN_API int Pep_GetVerboseFlag(void);
#endif
+// pyerrors.h
+#if defined(Py_LIMITED_API) || PY_VERSION_HEX < 0x030C0000
+LIBSHIBOKEN_API PyObject *PepErr_GetRaisedException();
+#else
+# define PepErr_GetRaisedException PyErr_GetRaisedException
+#endif
+
/*****************************************************************************
*
* RESOLVED: unicodeobject.h
# define PepCode_GET_FLAGS(o) PepCode_Get(o, "co_flags")
# define PepCode_GET_ARGCOUNT(o) PepCode_Get(o, "co_argcount")
+LIBSHIBOKEN_API PyObject *PepFunction_GetDefaults(PyObject *function);
+
/* Masks for co_flags above */
# define CO_OPTIMIZED 0x0001
# define CO_NEWLOCALS 0x0002
// is no longer considered to be accessible, we treat it as such.
LIBSHIBOKEN_API int PepType_SetDict(PyTypeObject *type, PyObject *dict);
+LIBSHIBOKEN_API void *PepType_GetSlot(PyTypeObject *type, int aSlot);
+
/*****************************************************************************
*
* Module Initialization
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef PYOBJECTHOLDER_H
+#define PYOBJECTHOLDER_H
+
+#include "sbkpython.h"
+
+#include <cassert>
+#include <utility>
+
+namespace Shiboken
+{
+
+/// PyObjectHolder holds a PyObject pointer, keeping a reference decrementing
+/// its reference counter when destroyed. It makes sure to hold the GIL when
+/// releasing. It implements copy/move semantics and is mainly intended as a
+/// base class for functors holding a callable which can be passed around and
+/// stored in containers or moved from freely.
+/// For one-shot functors, release() can be invoked after the call.
+class PyObjectHolder
+{
+public:
+ PyObjectHolder() noexcept = default;
+
+ /// PyObjectHolder constructor.
+ /// \param pyobj A reference to a Python object
+ explicit PyObjectHolder(PyObject *pyObj) noexcept : m_pyObj(pyObj)
+ {
+ assert(pyObj != nullptr);
+ Py_INCREF(m_pyObj);
+ }
+
+ PyObjectHolder(const PyObjectHolder &o) noexcept : m_pyObj(o.m_pyObj)
+ {
+ Py_XINCREF(m_pyObj);
+ }
+
+ PyObjectHolder &operator=(const PyObjectHolder &o) noexcept
+ {
+ if (this != &o) {
+ m_pyObj = o.m_pyObj;
+ Py_XINCREF(m_pyObj);
+ }
+ return *this;
+ }
+
+ PyObjectHolder(PyObjectHolder &&o) noexcept : m_pyObj{std::exchange(o.m_pyObj, nullptr)} {}
+
+ PyObjectHolder &operator=(PyObjectHolder &&o) noexcept
+ {
+ m_pyObj = std::exchange(o.m_pyObj, nullptr);
+ return *this;
+ }
+
+ /// Decref the python reference
+ ~PyObjectHolder() { release(); }
+
+ [[nodiscard]] bool isNull() const { return m_pyObj == nullptr; }
+ [[nodiscard]] operator bool() const { return m_pyObj != nullptr; }
+
+ /// Returns the pointer of the Python object being held.
+ [[nodiscard]] PyObject *object() const { return m_pyObj; }
+ [[nodiscard]] operator PyObject *() const { return m_pyObj; }
+
+ [[nodiscard]] PyObject *operator->() { return m_pyObj; }
+
+protected:
+ void release()
+ {
+ if (m_pyObj != nullptr) {
+ assert(Py_IsInitialized());
+ auto gstate = PyGILState_Ensure();
+ Py_DECREF(m_pyObj);
+ PyGILState_Release(gstate);
+ m_pyObj = nullptr;
+ }
+ }
+
+private:
+ PyObject *m_pyObj = nullptr;
+};
+
+} // namespace Shiboken
+
+#endif // PYOBJECTHOLDER_H
static PyObject *tpNew(PyTypeObject *subtype, PyObject * /* args */, PyObject * /* kwds */)
{
- auto *me = reinterpret_cast<ShibokenContainer *>(subtype->tp_alloc(subtype, 0));
+ allocfunc allocFunc = reinterpret_cast<allocfunc>(PepType_GetSlot(subtype, Py_tp_alloc));
+ auto *me = reinterpret_cast<ShibokenContainer *>(allocFunc(subtype, 0));
auto *d = new ShibokenSequenceContainerPrivate;
d->m_list = new SequenceContainer;
d->m_ownsList = true;
static PyObject *tpNewInvalid(PyTypeObject * /* subtype */, PyObject * /* args */, PyObject * /* kwds */)
{
- PyErr_Format(PyExc_NotImplementedError,
+ return PyErr_Format(PyExc_NotImplementedError,
"Opaque containers of type '%s' cannot be instantiated.",
typeid(SequenceContainer).name());
- return nullptr;
}
static int tpInit(PyObject * /* self */, PyObject * /* args */, PyObject * /* kwds */)
if (d->m_ownsList)
delete d->m_list;
delete d;
- Py_TYPE(pySelf)->tp_base->tp_free(self);
+ auto freeFunc = reinterpret_cast<freefunc>(PepType_GetSlot(Py_TYPE(pySelf)->tp_base,
+ Py_tp_free));
+ freeFunc(self);
}
static Py_ssize_t sqLen(PyObject *self)
static PyObject *sqGetItem(PyObject *self, Py_ssize_t i)
{
auto *d = get(self);
- if (i < 0 || i >= Py_ssize_t(d->m_list->size())) {
- PyErr_SetString(PyExc_IndexError, "index out of bounds");
- return nullptr;
- }
+ if (i < 0 || i >= Py_ssize_t(d->m_list->size()))
+ return PyErr_Format(PyExc_IndexError, "index out of bounds");
auto it = std::cbegin(*d->m_list);
std::advance(it, i);
return ShibokenContainerValueConverter<value_type>::convertValueToPython(*it);
static PyObject *push_back(PyObject *self, PyObject *pyArg)
{
auto *d = get(self);
- if (!ShibokenContainerValueConverter<value_type>::checkValue(pyArg)) {
- PyErr_SetString(PyExc_TypeError, "wrong type passed to append.");
- return nullptr;
- }
- if (d->m_const) {
- PyErr_SetString(PyExc_TypeError, msgModifyConstContainer);
- return nullptr;
- }
+ if (!ShibokenContainerValueConverter<value_type>::checkValue(pyArg))
+ return PyErr_Format(PyExc_TypeError, "wrong type passed to append.");
+ if (d->m_const)
+ return PyErr_Format(PyExc_TypeError, msgModifyConstContainer);
OptionalValue value = ShibokenContainerValueConverter<value_type>::convertValueToCpp(pyArg);
if (!value.has_value())
static PyObject *push_front(PyObject *self, PyObject *pyArg)
{
auto *d = get(self);
- if (!ShibokenContainerValueConverter<value_type>::checkValue(pyArg)) {
- PyErr_SetString(PyExc_TypeError, "wrong type passed to append.");
- return nullptr;
- }
- if (d->m_const) {
- PyErr_SetString(PyExc_TypeError, msgModifyConstContainer);
- return nullptr;
- }
+ if (!ShibokenContainerValueConverter<value_type>::checkValue(pyArg))
+ return PyErr_Format(PyExc_TypeError, "wrong type passed to append.");
+ if (d->m_const)
+ return PyErr_Format(PyExc_TypeError, msgModifyConstContainer);
OptionalValue value = ShibokenContainerValueConverter<value_type>::convertValueToCpp(pyArg);
if (!value.has_value())
static PyObject *clear(PyObject *self)
{
auto *d = get(self);
- if (d->m_const) {
- PyErr_SetString(PyExc_TypeError, msgModifyConstContainer);
- return nullptr;
- }
+ if (d->m_const)
+ return PyErr_Format(PyExc_TypeError, msgModifyConstContainer);
d->m_list->clear();
Py_RETURN_NONE;
static PyObject *pop_back(PyObject *self)
{
auto *d = get(self);
- if (d->m_const) {
- PyErr_SetString(PyExc_TypeError, msgModifyConstContainer);
- return nullptr;
- }
+ if (d->m_const)
+ return PyErr_Format(PyExc_TypeError, msgModifyConstContainer);
d->m_list->pop_back();
Py_RETURN_NONE;
static PyObject *pop_front(PyObject *self)
{
auto *d = get(self);
- if (d->m_const) {
- PyErr_SetString(PyExc_TypeError, msgModifyConstContainer);
- return nullptr;
- }
+ if (d->m_const)
+ return PyErr_Format(PyExc_TypeError, msgModifyConstContainer);
d->m_list->pop_front();
Py_RETURN_NONE;
static PyObject *reserve(PyObject *self, PyObject *pyArg)
{
auto *d = get(self);
- if (PyLong_Check(pyArg) == 0) {
- PyErr_SetString(PyExc_TypeError, "wrong type passed to reserve().");
- return nullptr;
- }
- if (d->m_const) {
- PyErr_SetString(PyExc_TypeError, msgModifyConstContainer);
- return nullptr;
- }
+ if (PyLong_Check(pyArg) == 0)
+ return PyErr_Format(PyExc_TypeError, "wrong type passed to reserve().");
+ if (d->m_const)
+ return PyErr_Format(PyExc_TypeError, msgModifyConstContainer);
if constexpr (ShibokenContainerHasReserve<SequenceContainer>::value) {
const Py_ssize_t size = PyLong_AsSsize_t(pyArg);
d->m_list->reserve(size);
} else {
- PyErr_SetString(PyExc_TypeError, "Container does not support reserve().");
- return nullptr;
+ return PyErr_Format(PyExc_TypeError, "Container does not support reserve().");
}
Py_RETURN_NONE;
#include "sbkconverter.h"
#include "sbkconverter_p.h"
#include "sbkarrayconverter_p.h"
+#include "sbkmodule.h"
#include "basewrapper_p.h"
#include "bindingmanager.h"
#include "autodecref.h"
#include <string>
#include <unordered_map>
+#include <unordered_set>
static SbkConverter **PrimitiveTypeConverters;
addPythonToCppValueConversion(sotp->converter, pythonToCppFunc, isConvertibleToCppFunc);
}
+void addPythonToCppValueConversion(Shiboken::Module::TypeInitStruct typeStruct,
+ PythonToCppFunc pythonToCppFunc,
+ IsConvertibleToCppFunc isConvertibleToCppFunc)
+{
+ addPythonToCppValueConversion(typeStruct.type, pythonToCppFunc, isConvertibleToCppFunc);
+}
+
PyObject *pointerToPython(PyTypeObject *type, const void *cppIn)
{
auto *sotp = PepType_SOTP(type);
return {};
}
+PythonToCppConversion pythonToCppPointerConversion(Module::TypeInitStruct typeStruct, PyObject *pyIn)
+{
+ return pythonToCppPointerConversion(typeStruct.type, pyIn);
+}
+
static inline PythonToCppFunc IsPythonToCppConvertible(const SbkConverter *converter, PyObject *pyIn)
{
assert(pyIn);
converters.insert(std::make_pair(typeName, converter));
}
-SbkConverter *getConverter(const char *typeName)
+static std::string getRealTypeName(const std::string &typeName)
{
- ConvertersMap::const_iterator it = converters.find(typeName);
+ auto size = typeName.size();
+ if (std::isalnum(typeName[size - 1]) == 0)
+ return typeName.substr(0, size - 1);
+ return typeName;
+}
+
+// PYSIDE-2404: Build a negative cache of already failed lookups.
+// The resulting list must be reset after each new import,
+// because that can change results. Also clear the cache after
+// reaching some threashold.
+static std::unordered_set<std::string> nonExistingTypeNames{};
+
+// Arbitrary size limit to prevent random name overflows.
+static constexpr std::size_t negativeCacheLimit = 50;
+
+static void rememberAsNonexistent(const std::string &typeName)
+{
+ if (nonExistingTypeNames.size() > negativeCacheLimit)
+ clearNegativeLazyCache();
+ converters.insert(std::make_pair(typeName, nullptr));
+ nonExistingTypeNames.insert(typeName);
+}
+
+SbkConverter *getConverter(const char *typeNameC)
+{
+ std::string typeName = typeNameC;
+ auto it = converters.find(typeName);
+ // PYSIDE-2404: This can also contain explicit nullptr as a negative cache.
+ if (it != converters.end())
+ return it->second;
+ // PYSIDE-2404: Did not find the name. Load the lazy classes
+ // which have this name and try again.
+ Shiboken::Module::loadLazyClassesWithName(getRealTypeName(typeName).c_str());
+ it = converters.find(typeName);
if (it != converters.end())
return it->second;
+ // Cache the negative result. Don't forget to clear the cache for new modules.
+ rememberAsNonexistent(typeName);
+
if (Shiboken::pyVerbose() > 0) {
const std::string message =
std::string("Can't find type resolver for type '") + typeName + "'.";
return nullptr;
}
+void clearNegativeLazyCache()
+{
+ for (const auto &typeName : nonExistingTypeNames) {
+ auto it = converters.find(typeName);
+ converters.erase(it);
+ }
+ nonExistingTypeNames.clear();
+}
+
SbkConverter *primitiveTypeConverter(int index)
{
return PrimitiveTypeConverters[index];
#define SBK_CONVERTER_H
#include "sbkpython.h"
+#include "sbkmodule.h"
#include "shibokenmacros.h"
#include "sbkenum.h"
#include "basewrapper_p.h"
LIBSHIBOKEN_API void addPythonToCppValueConversion(PyTypeObject *type,
PythonToCppFunc pythonToCppFunc,
IsConvertibleToCppFunc isConvertibleToCppFunc);
+LIBSHIBOKEN_API void addPythonToCppValueConversion(Shiboken::Module::TypeInitStruct typeStruct,
+ PythonToCppFunc pythonToCppFunc,
+ IsConvertibleToCppFunc isConvertibleToCppFunc);
// C++ -> Python ---------------------------------------------------------------------------
*/
LIBSHIBOKEN_API PythonToCppFunc isPythonToCppPointerConvertible(PyTypeObject *type, PyObject *pyIn);
LIBSHIBOKEN_API PythonToCppConversion pythonToCppPointerConversion(PyTypeObject *type, PyObject *pyIn);
+LIBSHIBOKEN_API PythonToCppConversion pythonToCppPointerConversion(Module::TypeInitStruct typeStruct, PyObject *pyIn);
/**
* Returns a Python to C++ conversion function if the Python object is convertible to a C++ value.
}
static void toCpp(PyObject *pyIn, void *cppOut)
{
- *reinterpret_cast<FLOAT *>(cppOut) = FLOAT(PyLong_AsLongLong(pyIn));
+ *reinterpret_cast<FLOAT *>(cppOut) = FLOAT(PyLong_AsDouble(pyIn));
}
static PythonToCppFunc isConvertible(PyObject *pyIn)
{
IsConvertibleToCppFunc toCppPointerCheckFunc,
CppToPythonFunc pointerToPythonFunc,
CppToPythonFunc copyToPythonFunc);
+
+/// Interface for sbkmodule which must reset cache when new module is loaded.
+LIBSHIBOKEN_API void clearNegativeLazyCache();
+
} // namespace Shiboken::Conversions
#endif // SBK_CONVERTER_P_H
return PyUnicode_FromStringAndSize(value.data(), value.size());
}
+PyObject *fromCppStringView(std::string_view value)
+{
+ return PyUnicode_FromStringAndSize(value.data(), value.size());
+}
+
PyObject *fromCppWString(const std::wstring &value)
{
return PyUnicode_FromWideChar(value.data(), value.size());
#include "shibokenmacros.h"
#include <string>
+#include <string_view>
namespace Shiboken::String
{
LIBSHIBOKEN_API PyObject *fromCppString(const std::string &value);
+ LIBSHIBOKEN_API PyObject *fromCppStringView(std::string_view value);
LIBSHIBOKEN_API PyObject *fromCppWString(const std::wstring &value);
LIBSHIBOKEN_API void toCppString(PyObject *str, std::string *value);
LIBSHIBOKEN_API void toCppWString(PyObject *str, std::wstring *value);
PyObject *createDoubleArray1(Py_ssize_t, const double *)
{
- return Py_None;
+ Py_RETURN_NONE;
}
PyObject *createFloatArray1(Py_ssize_t, const float *)
{
- return Py_None;
+ Py_RETURN_NONE;
}
PyObject *createIntArray1(Py_ssize_t, const int *)
{
- return Py_None;
+ Py_RETURN_NONE;
}
#endif // !HAVE_NUMPY
#include "sbkenum.h"
#include "sbkstring.h"
+#include "helper.h"
#include "sbkstaticstrings.h"
#include "sbkstaticstrings_p.h"
#include "sbkconverter.h"
enumName = PyDict_GetItem(sotp->enumTypeDict, name);
}
+ SBK_UNUSED(getPyEnumMeta()); // enforce PyEnumModule creation
+ assert(PyEnumModule != nullptr);
AutoDecRef PyEnumType(PyObject_GetAttr(PyEnumModule, enumName));
assert(PyEnumType.object());
bool isFlag = PyObject_IsSubclass(PyEnumType, PyFlag);
#include "basewrapper.h"
#include "basewrapper_p.h"
#include "autodecref.h"
+#include "pep384ext.h"
#include "sbkenum.h"
#include "sbkstring.h"
#include "sbkstaticstrings.h"
}
static SelectableFeatureHook SelectFeatureSet = nullptr;
+static SelectableFeatureCallback featureCb = nullptr;
+
+void setSelectableFeatureCallback(SelectableFeatureCallback func)
+{
+ featureCb = func;
+}
SelectableFeatureHook initSelectableFeature(SelectableFeatureHook func)
{
auto ret = SelectFeatureSet;
SelectFeatureSet = func;
+ if (featureCb)
+ featureCb(SelectFeatureSet != nullptr);
return ret;
}
//
static PyObject *dismodule = PyImport_ImportModule("dis");
static PyObject *disco = PyObject_GetAttrString(dismodule, "disco");
static PyObject *const _f_lasti = Shiboken::String::createStaticString("f_lasti");
+ static PyObject *const _f_lineno = Shiboken::String::createStaticString("f_lineno");
static PyObject *const _f_code = Shiboken::String::createStaticString("f_code");
- auto *frame = reinterpret_cast<PyObject *>(PyEval_GetFrame());
- AutoDecRef f_lasti(PyObject_GetAttr(frame, _f_lasti));
- AutoDecRef f_code(PyObject_GetAttr(frame, _f_code));
+ static PyObject *const _co_filename = Shiboken::String::createStaticString("co_filename");
AutoDecRef ignore{};
- fprintf(stdout, "\n%s BEGIN\n", marker);
- ignore.reset(PyObject_CallFunctionObjArgs(disco, f_code.object(), f_lasti.object(), nullptr));
- fprintf(stdout, "%s END\n\n", marker);
+ auto *frame = reinterpret_cast<PyObject *>(PyEval_GetFrame());
+ if (frame == nullptr) {
+ fprintf(stdout, "\n%s BEGIN no frame END\n\n", marker);
+ } else {
+ AutoDecRef f_lasti(PyObject_GetAttr(frame, _f_lasti));
+ AutoDecRef f_lineno(PyObject_GetAttr(frame, _f_lineno));
+ AutoDecRef f_code(PyObject_GetAttr(frame, _f_code));
+ AutoDecRef co_filename(PyObject_GetAttr(f_code, _co_filename));
+ long line = PyLong_AsLong(f_lineno);
+ const char *fname = String::toCString(co_filename);
+ fprintf(stdout, "\n%s BEGIN line=%ld %s\n", marker, line, fname);
+ ignore.reset(PyObject_CallFunctionObjArgs(disco, f_code.object(), f_lasti.object(), nullptr));
+ fprintf(stdout, "%s END line=%ld %s\n\n", marker, line, fname);
+ }
#if PY_VERSION_HEX >= 0x030C0000 && !Py_LIMITED_API
if (error_type)
PyErr_DisplayException(error_value);
// We look into the currently active operation if we are going to call
// a method with zero arguments.
auto *frame = PyEval_GetFrame();
-#if PY_VERSION_HEX >= 0x03090000 && !Py_LIMITED_API && !defined(PYPY_VERSION)
+#if !Py_LIMITED_API && !defined(PYPY_VERSION)
auto *f_code = PyFrame_GetCode(frame);
#else
static PyObject *const _f_code = Shiboken::String::createStaticString("f_code");
* with the complex `tp_getattro` of `QObject` and other instances.
* What we change here is the meta class of `QObject`.
*/
- static getattrofunc const type_getattro = PyType_Type.tp_getattro;
+ static getattrofunc const type_getattro = PepExt_Type_GetGetAttroSlot(&PyType_Type);
static PyObject *const ignAttr1 = PyName::qtStaticMetaObject();
static PyObject *const ignAttr2 = PyMagicName::get();
static PyTypeObject *const EnumMeta = getPyEnumMeta();
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "sbkmodule.h"
+#include "autodecref.h"
#include "basewrapper.h"
#include "bindingmanager.h"
+#include "sbkstring.h"
+#include "sbkcppstring.h"
+#include "sbkconverter_p.h"
+
#include <unordered_map>
+#include <unordered_set>
+#include <cstring>
/// This hash maps module objects to arrays of Python types.
-using ModuleTypesMap = std::unordered_map<PyObject *, PyTypeObject **> ;
+using ModuleTypesMap = std::unordered_map<PyObject *, Shiboken::Module::TypeInitStruct *> ;
/// This hash maps module objects to arrays of converters.
using ModuleConvertersMap = std::unordered_map<PyObject *, SbkConverter **>;
+/// This hash maps type names to type creation functions.
+using TypeCreationFunctionModulePair =
+ std::pair<Shiboken::Module::TypeCreationFunction, PyObject *>;
+using NameToTypeFunctionMap = std::unordered_map<std::string, TypeCreationFunctionModulePair>;
+
+/// This hash maps module objects to maps of names to functions.
+using ModuleToFuncsMap = std::unordered_map<PyObject *, NameToTypeFunctionMap> ;
+
/// All types produced in imported modules are mapped here.
static ModuleTypesMap moduleTypes;
static ModuleConvertersMap moduleConverters;
+static ModuleToFuncsMap moduleToFuncs;
namespace Shiboken
{
namespace Module
{
+// PYSIDE-2404: Replacing the arguments generated by cpythonTypeNameExt
+// by a function call.
+LIBSHIBOKEN_API PyTypeObject *get(TypeInitStruct &typeStruct)
+{
+ if (typeStruct.type != nullptr)
+ return typeStruct.type;
+
+ static PyObject *sysModules = PyImport_GetModuleDict();
+
+ // The slow path for initialization.
+ // We get the type by following the chain from the module.
+ // As soon as types[index] gets filled, we can stop.
+
+ std::string_view names(typeStruct.fullName);
+ const bool usePySide = names.compare(0, 8, "PySide6.") == 0;
+ auto dotPos = usePySide ? names.find('.', 8) : names.find('.');
+ auto startPos = dotPos + 1;
+ AutoDecRef modName(String::fromCppStringView(names.substr(0, dotPos)));
+ auto *modOrType = PyDict_GetItem(sysModules, modName);
+ if (modOrType == nullptr) {
+ PyErr_Format(PyExc_SystemError, "Module \"%U\" should already be in sys.modules",
+ modName.object());
+ return nullptr;
+ }
+
+ do {
+ dotPos = names.find('.', startPos);
+ auto typeName = dotPos != std::string::npos
+ ? names.substr(startPos, dotPos - startPos)
+ : names.substr(startPos);
+ startPos = dotPos + 1;
+ AutoDecRef obTypeName(String::fromCppStringView(typeName));
+ modOrType = PyObject_GetAttr(modOrType, obTypeName);
+ } while (typeStruct.type == nullptr && dotPos != std::string::npos);
+
+ return typeStruct.type;
+}
+
+static PyTypeObject *incarnateType(PyObject *module, const char *name,
+ NameToTypeFunctionMap &nameToFunc)
+{
+ // - locate the name and retrieve the generating function
+ auto funcIter = nameToFunc.find(name);
+ if (funcIter == nameToFunc.end()) {
+ // attribute does really not exist.
+ PyErr_SetNone(PyExc_AttributeError);
+ return nullptr;
+ }
+ // - call this function that returns a PyTypeObject
+ auto pair = funcIter->second;
+ auto initFunc = pair.first;
+ auto *modOrType = pair.second;
+
+ // PYSIDE-2404: Make sure that no switching happens during type creation.
+ auto saveFeature = initSelectableFeature(nullptr);
+ PyTypeObject *type = initFunc(modOrType);
+ initSelectableFeature(saveFeature);
+
+ // - assign this object to the name in the module
+ auto *res = reinterpret_cast<PyObject *>(type);
+ Py_INCREF(res);
+ PyModule_AddObject(module, name, res); // steals reference
+ // - remove the entry, if not by something cleared.
+ if (!nameToFunc.empty())
+ nameToFunc.erase(funcIter);
+ // - return the PyTypeObject.
+ return type;
+}
+
+// PYSIDE-2404: Make sure that the mentioned classes really exist.
+// Used in `Pyside::typeName`. Because the result will be cached by
+// the creation of the type(s), this is efficient.
+void loadLazyClassesWithName(const char *name)
+{
+ for (auto const & tableIter : moduleToFuncs) {
+ auto nameToFunc = tableIter.second;
+ auto funcIter = nameToFunc.find(name);
+ if (funcIter != nameToFunc.end()) {
+ // attribute exists in the lazy types.
+ auto *module = tableIter.first;
+ incarnateType(module, name, nameToFunc);
+ }
+ }
+}
+
+// PYSIDE-2404: Completely load all not yet loaded classes.
+// This is needed to resolve a star import.
+void resolveLazyClasses(PyObject *module)
+{
+ // - locate the module in the moduleTofuncs mapping
+ auto tableIter = moduleToFuncs.find(module);
+ if (tableIter == moduleToFuncs.end())
+ return;
+
+ // - see if there are still unloaded elements
+ auto &nameToFunc = tableIter->second;
+
+ // - incarnate all types.
+ while (!nameToFunc.empty()) {
+ auto it = nameToFunc.begin();
+ auto attrNameStr = it->first;
+ incarnateType(module, attrNameStr.c_str(), nameToFunc);
+ }
+}
+
+// PYSIDE-2404: Override the gettattr function of modules.
+static getattrofunc origModuleGetattro{};
+
+// PYSIDE-2404: Use the patched module getattr to do on-demand initialization.
+// This modifies _all_ modules but should have no impact.
+static PyObject *PyModule_lazyGetAttro(PyObject *module, PyObject *name)
+{
+ // - check if the attribute is present and return it.
+ auto *attr = PyObject_GenericGetAttr(module, name);
+ // - we handle AttributeError, only.
+ if (!(attr == nullptr && PyErr_ExceptionMatches(PyExc_AttributeError)))
+ return attr;
+
+ PyErr_Clear();
+ // - locate the module in the moduleTofuncs mapping
+ auto tableIter = moduleToFuncs.find(module);
+ // - if this is not our module, use the original
+ if (tableIter == moduleToFuncs.end())
+ return origModuleGetattro(module, name);
+
+ // - locate the name and retrieve the generating function
+ const char *attrNameStr = Shiboken::String::toCString(name);
+ auto &nameToFunc = tableIter->second;
+ // - create the real type (incarnateType checks this)
+ auto *type = incarnateType(module, attrNameStr, nameToFunc);
+ auto *ret = reinterpret_cast<PyObject *>(type);
+ // - if attribute does really not exist use the original
+ if (ret == nullptr && PyErr_ExceptionMatches(PyExc_AttributeError)) {
+ PyErr_Clear();
+ return origModuleGetattro(module, name);
+ }
+
+ return ret;
+}
+
+// PYSIDE-2404: Supply a new module dir for not yet visible entries.
+// This modification is only for "our" modules.
+static PyObject *_module_dir_template(PyObject * /* self */, PyObject *args)
+{
+ static PyObject *const _dict = Shiboken::String::createStaticString("__dict__");
+ // The dir function must replace all of the builtin function.
+ PyObject *module{};
+ if (!PyArg_ParseTuple(args, "O", &module))
+ return nullptr;
+
+ auto tableIter = moduleToFuncs.find(module);
+ assert(tableIter != moduleToFuncs.end());
+ Shiboken::AutoDecRef dict(PyObject_GetAttr(module, _dict));
+ auto *ret = PyDict_Keys(dict);
+ // Now add all elements that were not yet in the dict.
+ auto &nameToFunc = tableIter->second;
+ for (const auto &funcIter : nameToFunc) {
+ const char *name = funcIter.first.c_str();
+ Shiboken::AutoDecRef pyName(PyUnicode_FromString(name));
+ PyList_Append(ret, pyName);
+ }
+ return ret;
+}
+
+static PyMethodDef module_methods[] = {
+ {"__dir__", (PyCFunction)_module_dir_template, METH_VARARGS, nullptr},
+ {nullptr, nullptr, 0, nullptr}
+};
+
+// Python 3.8 - 3.12
+static int const LOAD_CONST_312 = 100;
+static int const IMPORT_NAME_312 = 108;
+
+static bool isImportStar(PyObject *module)
+{
+ // Find out whether we have a star import. This must work even
+ // when we have no import support from feature.
+ static PyObject *const _f_code = Shiboken::String::createStaticString("f_code");
+ static PyObject *const _f_lasti = Shiboken::String::createStaticString("f_lasti");
+ static PyObject *const _f_back = Shiboken::String::createStaticString("f_back");
+ static PyObject *const _co_code = Shiboken::String::createStaticString("co_code");
+ static PyObject *const _co_consts = Shiboken::String::createStaticString("co_consts");
+ static PyObject *const _co_names = Shiboken::String::createStaticString("co_names");
+
+ auto *obFrame = reinterpret_cast<PyObject *>(PyEval_GetFrame());
+ if (obFrame == nullptr)
+ return true; // better assume worst-case.
+
+ Py_INCREF(obFrame);
+ AutoDecRef dec_frame(obFrame);
+
+ // Calculate the offset of the running import_name opcode on the stack.
+ // Right before that there must be a load_const with the tuple `("*",)`.
+ while (dec_frame.object() != Py_None) {
+ AutoDecRef dec_f_code(PyObject_GetAttr(dec_frame, _f_code));
+ AutoDecRef dec_co_code(PyObject_GetAttr(dec_f_code, _co_code));
+ AutoDecRef dec_f_lasti(PyObject_GetAttr(dec_frame, _f_lasti));
+ Py_ssize_t f_lasti = PyLong_AsSsize_t(dec_f_lasti);
+ Py_ssize_t code_len;
+ char *co_code{};
+ PyBytes_AsStringAndSize(dec_co_code, &co_code, &code_len);
+ uint8_t opcode2 = co_code[f_lasti];
+ uint8_t opcode1 = co_code[f_lasti - 2];
+ if (opcode1 == LOAD_CONST_312 && opcode2 == IMPORT_NAME_312) {
+ uint8_t oparg1 = co_code[f_lasti - 1];
+ uint8_t oparg2 = co_code[f_lasti + 1];
+ AutoDecRef dec_co_consts(PyObject_GetAttr(dec_f_code, _co_consts));
+ auto *fromlist = PyTuple_GetItem(dec_co_consts, oparg1);
+ if (PyTuple_Check(fromlist) && PyTuple_Size(fromlist) == 1
+ && Shiboken::String::toCString(PyTuple_GetItem(fromlist, 0))[0] == '*') {
+ AutoDecRef dec_co_names(PyObject_GetAttr(dec_f_code, _co_names));
+ const char *name = String::toCString(PyTuple_GetItem(dec_co_names, oparg2));
+ const char *modName = PyModule_GetName(module);
+ if (std::strcmp(name, modName) == 0)
+ return true;
+ }
+ }
+ dec_frame.reset(PyObject_GetAttr(dec_frame, _f_back));
+ }
+ return false;
+}
+
+// PYSIDE-2404: These modules produce ambiguous names which we cannot handle, yet.
+static std::unordered_set<std::string> dontLazyLoad{
+ "sample",
+ "smart",
+ "testbinding"
+};
+
+static const std::unordered_set<std::string> knownModules{
+ "shiboken6.Shiboken",
+ "minimal",
+ "other",
+ "sample",
+ "smart",
+ "scriptableapplication",
+ "testbinding"
+};
+
+static bool canNotLazyLoad(PyObject *module)
+{
+ const char *modName = PyModule_GetName(module);
+
+ // There are no more things that must be disabled :-D
+ return dontLazyLoad.find(modName) != dontLazyLoad.end();
+}
+
+static bool shouldLazyLoad(PyObject *module)
+{
+ const char *modName = PyModule_GetName(module);
+
+ if (knownModules.find(modName) != knownModules.end())
+ return true;
+ return std::strncmp(modName, "PySide6.", 8) == 0;
+}
+
+static int lazyLoadDefault()
+{
+#ifndef PYPY_VERSION
+ int result = 1;
+#else
+ int result = 0;
+#endif
+ if (auto *flag = getenv("PYSIDE6_OPTION_LAZY"))
+ result = std::atoi(flag);
+ return result;
+}
+
+void AddTypeCreationFunction(PyObject *module,
+ const char *name,
+ TypeCreationFunction func)
+{
+ static const int value = lazyLoadDefault();
+
+ // - locate the module in the moduleTofuncs mapping
+ auto tableIter = moduleToFuncs.find(module);
+ assert(tableIter != moduleToFuncs.end());
+ // - Assign the name/generating function pair.
+ auto &nameToFunc = tableIter->second;
+ TypeCreationFunctionModulePair pair{func, module};
+ auto nit = nameToFunc.find(name);
+ if (nit == nameToFunc.end())
+ nameToFunc.insert(std::make_pair(name, pair));
+ else
+ nit->second = pair;
+
+ // PYSIDE-2404: Lazy Loading
+ //
+ // Options:
+ // 0 - switch lazy loading off.
+ // 1 - lazy loading for all known modules.
+ // 3 - lazy loading for any module.
+ //
+ // By default we lazy load all known modules (option = 1).
+
+ if (value == 0 // completely disabled
+ || canNotLazyLoad(module) // for some reason we cannot lazy load
+ || (value == 1 && !shouldLazyLoad(module)) // not a known module
+ ) {
+ PyTypeObject *type = func(module);
+ PyModule_AddObject(module, name, reinterpret_cast<PyObject *>(type)); // steals reference
+ }
+}
+
+void AddTypeCreationFunction(PyObject *module,
+ const char *name,
+ TypeCreationFunction func,
+ const char *containerName)
+{
+ // This version could be delayed as well, but for the few cases
+ // we simply fetch the container type and insert directly.
+ AutoDecRef obContainerType(PyObject_GetAttrString(module, containerName));
+ PyTypeObject *type = func(obContainerType);
+ PyObject_SetAttrString(obContainerType, name, reinterpret_cast<PyObject *>(type)); // steals reference
+}
+
+void AddTypeCreationFunction(PyObject *module,
+ const char *name,
+ TypeCreationFunction func,
+ const char *outerContainerName,
+ const char *innerContainerName)
+{
+ // This version has even more indirection. It is very rare, and
+ // we handle it directly.
+ AutoDecRef obOuterType(PyObject_GetAttrString(module, outerContainerName));
+ AutoDecRef obInnerType(PyObject_GetAttrString(obOuterType, innerContainerName));
+ PyTypeObject *type = func(obInnerType);
+ PyObject_SetAttrString(obInnerType, name, reinterpret_cast<PyObject *>(type)); // steals reference
+}
+
+void AddTypeCreationFunction(PyObject *module,
+ const char *name,
+ TypeCreationFunction func,
+ const char *containerName3,
+ const char *containerName2,
+ const char *containerName)
+{
+ // This version has even mode indirection. It is very rare, and
+ // we handle it directly.
+ AutoDecRef obContainerType3(PyObject_GetAttrString(module, containerName3));
+ AutoDecRef obContainerType2(PyObject_GetAttrString(obContainerType3, containerName2));
+ AutoDecRef obContainerType(PyObject_GetAttrString(obContainerType2, containerName));
+ PyTypeObject *type = func(obContainerType);
+ PyObject_SetAttrString(obContainerType, name, reinterpret_cast<PyObject *>(type)); // steals reference
+}
+
PyObject *import(const char *moduleName)
{
PyObject *sysModules = PyImport_GetModuleDict();
PyObject *module = PyDict_GetItemString(sysModules, moduleName);
- if (module)
+ if (module != nullptr)
Py_INCREF(module);
else
module = PyImport_ImportModule(moduleName);
- if (!module)
- PyErr_Format(PyExc_ImportError,"could not import module '%s'", moduleName);
+ if (module == nullptr)
+ PyErr_Format(PyExc_ImportError, "could not import module '%s'", moduleName);
return module;
}
-PyObject *create(const char * /* moduleName */, void *moduleData)
+// PYSIDE-2404: Redirecting import for "import *" support.
+//
+// The first import will be handled by the isImportStar function.
+// But the same module might be imported twice, which would give no
+// introspection due to module caching.
+
+static PyObject *origImportFunc{};
+
+static PyObject *lazy_import(PyObject * /* self */, PyObject *args, PyObject *kwds)
+{
+ auto *ret = PyObject_Call(origImportFunc, args, kwds);
+ if (ret != nullptr) {
+ // PYSIDE-2404: Support star import when lazy loading.
+ if (PyTuple_Size(args) >= 4) {
+ auto *fromlist = PyTuple_GetItem(args, 3);
+ if (PyTuple_Check(fromlist) && PyTuple_Size(fromlist) == 1
+ && Shiboken::String::toCString(PyTuple_GetItem(fromlist, 0))[0] == '*')
+ Shiboken::Module::resolveLazyClasses(ret);
+ }
+ }
+ return ret;
+}
+
+static PyMethodDef lazy_methods[] = {
+ {"__lazy_import__", (PyCFunction)lazy_import, METH_VARARGS | METH_KEYWORDS, nullptr},
+ {nullptr, nullptr, 0, nullptr}
+};
+
+PyObject *create(const char * /* modName */, void *moduleData)
{
+ static auto *sysModules = PyImport_GetModuleDict();
+ static auto *builtins = PyEval_GetBuiltins();
+ static auto *partial = Pep_GetPartialFunction();
+ static bool lazy_init{};
+
Shiboken::init();
- return PyModule_Create(reinterpret_cast<PyModuleDef *>(moduleData));
+ auto *module = PyModule_Create(reinterpret_cast<PyModuleDef *>(moduleData));
+
+ // Setup of a dir function for "missing" classes.
+ auto *moduleDirTemplate = PyCFunction_NewEx(module_methods, nullptr, nullptr);
+ // Turn this function into a bound object, so we have access to the module.
+ auto *moduleDir = PyObject_CallFunctionObjArgs(partial, moduleDirTemplate, module, nullptr);
+ PyModule_AddObject(module, module_methods->ml_name, moduleDir); // steals reference
+ // Insert an initial empty table for the module.
+ NameToTypeFunctionMap empty;
+ moduleToFuncs.insert(std::make_pair(module, empty));
+
+ // A star import must be done unconditionally. Use the complete name.
+ if (isImportStar(module))
+ dontLazyLoad.insert(PyModule_GetName(module));
+
+ if (!lazy_init) {
+ // Install the getattr patch.
+ origModuleGetattro = PyModule_Type.tp_getattro;
+ PyModule_Type.tp_getattro = PyModule_lazyGetAttro;
+ // Add the lazy import redirection, keeping a reference.
+ origImportFunc = PyDict_GetItemString(builtins, "__import__");
+ Py_INCREF(origImportFunc);
+ AutoDecRef func(PyCFunction_NewEx(lazy_methods, nullptr, nullptr));
+ PyDict_SetItemString(builtins, "__import__", func);
+ // Everything is set.
+ lazy_init = true;
+ }
+ // PYSIDE-2404: Nuitka inserts some additional code in standalone mode
+ // in an invisible virtual module (i.e. `QtCore-postLoad`)
+ // that gets imported before the running import can call
+ // `_PyImport_FixupExtensionObject` which does the insertion
+ // into `sys.modules`. This can cause a race condition.
+ // Insert the module early into the module dict to prevend recursion.
+ PyDict_SetItemString(sysModules, PyModule_GetName(module), module);
+ // Clear the non-existing name cache because we have a new module.
+ Shiboken::Conversions::clearNegativeLazyCache();
+ return module;
}
-void registerTypes(PyObject *module, PyTypeObject **types)
+void registerTypes(PyObject *module, TypeInitStruct *types)
{
auto iter = moduleTypes.find(module);
if (iter == moduleTypes.end())
moduleTypes.insert(std::make_pair(module, types));
}
-PyTypeObject **getTypes(PyObject *module)
+TypeInitStruct *getTypes(PyObject *module)
{
auto iter = moduleTypes.find(module);
return (iter == moduleTypes.end()) ? 0 : iter->second;
namespace Shiboken::Module {
+struct TypeInitStruct
+{
+ PyTypeObject *type;
+ const char *fullName;
+};
+
+/// PYSIDE-2404: Replacing the arguments in cpythonTypeNameExt by a function.
+LIBSHIBOKEN_API PyTypeObject *get(TypeInitStruct &typeStruct);
+
+/// PYSIDE-2404: Make sure that mentioned classes really exist.
+LIBSHIBOKEN_API void loadLazyClassesWithName(const char *name);
+
+/// PYSIDE-2404: incarnate all classes for star imports.
+LIBSHIBOKEN_API void resolveLazyClasses(PyObject *module);
+
/**
* Imports and returns the module named \p moduleName, or a NULL pointer in case of failure.
* If the module is already imported, it increments its reference count before returning it.
*/
LIBSHIBOKEN_API PyObject *create(const char *moduleName, void *moduleData);
+using TypeCreationFunction = PyTypeObject *(*)(PyObject *module);
+
+/// Adds a type creation function to the module.
+LIBSHIBOKEN_API void AddTypeCreationFunction(PyObject *module,
+ const char *name,
+ TypeCreationFunction func);
+
+LIBSHIBOKEN_API void AddTypeCreationFunction(PyObject *module,
+ const char *name,
+ TypeCreationFunction func,
+ const char *containerName);
+
+LIBSHIBOKEN_API void AddTypeCreationFunction(PyObject *module,
+ const char *name,
+ TypeCreationFunction func,
+ const char *outerContainerName,
+ const char *innerContainerName);
+
+LIBSHIBOKEN_API void AddTypeCreationFunction(PyObject *module,
+ const char *name,
+ TypeCreationFunction func,
+ const char *containerName3,
+ const char *containerName2,
+ const char *containerName);
/**
* Registers the list of types created by \p module.
* \param module Module where the types were created.
* \param types Array of PyTypeObject *objects representing the types created on \p module.
*/
-LIBSHIBOKEN_API void registerTypes(PyObject *module, PyTypeObject **types);
+LIBSHIBOKEN_API void registerTypes(PyObject *module, TypeInitStruct *types);
/**
* Retrieves the array of types.
* \param module Module where the types were created.
* \returns A pointer to the PyTypeObject *array of types.
*/
-LIBSHIBOKEN_API PyTypeObject **getTypes(PyObject *module);
+LIBSHIBOKEN_API TypeInitStruct *getTypes(PyObject *module);
/**
* Registers the list of converters created by \p module for non-wrapper types.
namespace Shiboken::Numpy
{
+#ifdef HAVE_NUMPY
+static void initNumPy()
+{
+ // PYSIDE-2404: Delay-initialize numpy from check() as it causes a
+ // significant startup delay (~770 allocations in memray)
+ static bool initialized = false;
+ if (initialized)
+ return;
+ initialized = true;
+ // Expanded from macro "import_array" in __multiarray_api.h
+ // Make sure to read about the magic defines PY_ARRAY_UNIQUE_SYMBOL etc.,
+ // when changing this or spreading the code over several source files.
+ if (_import_array() < 0)
+ PyErr_Print();
+}
+#endif // HAVE_NUMPY
+
bool check(PyObject *pyIn)
{
#ifdef HAVE_NUMPY
+ initNumPy();
return PyArray_Check(pyIn);
#else
SBK_UNUSED(pyIn);
template <int dimension>
static bool isPrimitiveArray(PyObject *pyIn, int expectedNpType)
{
+ Shiboken::Numpy::initNumPy();
if (!PyArray_Check(pyIn))
return false;
auto *pya = reinterpret_cast<PyArrayObject *>(pyIn);
template <class T>
static void setOrExtendArrayConverter(int dimension, IsArrayConvertibleToCppFunc toCppCheckFunc)
{
+ // PYSIDE-2404/FIXME: When adding a C++ -> Python conversion, be sure
+ // to delay-initialize numpy in the converter (similar to the
+ // initialization in check() for the Python -> C++ conversion).
SbkArrayConverter *arrayConverter = ArrayTypeConverter<T>(dimension);
if (arrayConverter == unimplementedArrayConverter()) {
arrayConverter = createArrayConverter(toCppCheckFunc);
void initNumPyArrayConverters()
{
- // Expanded from macro "import_array" in __multiarray_api.h
- // Make sure to read about the magic defines PY_ARRAY_UNIQUE_SYMBOL etc.,
- // when changing this or spreading the code over several source files.
- if (_import_array() < 0) {
- if (debugNumPy)
- PyErr_Print();
- PyErr_Clear();
- return;
- }
// Extend the converters for primitive types by NumPy ones.
extendArrayConverter1<short, NPY_SHORT>();
extendArrayConverter2<short, NPY_SHORT>();
STATIC_STRING_IMPL(loads, "loads")
STATIC_STRING_IMPL(multi, "multi")
STATIC_STRING_IMPL(name, "name")
+STATIC_STRING_IMPL(orig_dict, "orig_dict")
STATIC_STRING_IMPL(qApp, "qApp")
STATIC_STRING_IMPL(result, "result")
STATIC_STRING_IMPL(select_id, "select_id")
LIBSHIBOKEN_API PyObject *loads();
LIBSHIBOKEN_API PyObject *multi();
LIBSHIBOKEN_API PyObject *name();
+LIBSHIBOKEN_API PyObject *orig_dict();
LIBSHIBOKEN_API PyObject *result();
LIBSHIBOKEN_API PyObject *select_id();
LIBSHIBOKEN_API PyObject *value();
}
}
+#if !defined(Py_LIMITED_API) && PY_VERSION_HEX >= 0x030C0000
+ auto *ret = PyType_FromMetaclass(meta, nullptr /*module*/, spec, bases);
+#else
auto *ret = _PyType_FromSpecWithBases(spec, bases);
+#endif
if (keepMeta)
keepMeta->tp_new = keepNew;
#include "gilstate.h"
#include "threadstatesaver.h"
#include "helper.h"
+#include "pyobjectholder.h"
#include "sbkarrayconverter.h"
#include "sbkconverter.h"
#include "sbkenum.h"
return pyside_tp_get___signature__(ob, modifier);
if (Py_TYPE(ob) == &PyWrapperDescr_Type)
return pyside_wd_get___signature__(ob, modifier);
+ // For classmethods we use the simple wrapper description implementation.
+ if (Py_TYPE(ob) == &PyClassMethodDescr_Type)
+ return pyside_wd_get___signature__(ob, modifier);
return nullptr;
}
* Still, it is not possible to call init phase 2 from here,
* because the import is still running. Do it from Python!
*/
+ init_shibokensupport_module();
+
#ifndef PYPY_VERSION
static const bool patch_types = true;
#else
{
AutoDecRef func(name_key_to_func(ob_md));
if (func.object() == Py_None)
- return Py_None;
+ Py_RETURN_NONE;
if (func.isNull())
Py_FatalError("missing mapping in MethodDescriptor");
return pyside_cf_get___signature__(func, modifier);
auto *type = reinterpret_cast<PyTypeObject *>(obtype);
AutoDecRef tpDict(PepType_GetDict(type));
auto *dict = tpDict.object();
+
+ // PYSIDE-2404: Get the original dict for late initialization.
+ // The dict might have been switched before signature init.
+ static const auto *pyTypeType_tp_dict = PepType_GetDict(&PyType_Type);
+ if (Py_TYPE(dict) != Py_TYPE(pyTypeType_tp_dict)) {
+ tpDict.reset(PyObject_GetAttr(dict, PyName::orig_dict()));
+ dict = tpDict.object();
+ }
+
PyMethodDef *meth = type->tp_methods;
if (meth == nullptr)
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "voidptr.h"
+#include "pep384ext.h"
#include "sbkconverter.h"
#include "basewrapper.h"
#include "basewrapper_p.h"
// like this, actual call forgotten:
// SbkVoidPtrObject *self =
// reinterpret_cast<SbkVoidPtrObject *>(type->tp_alloc);
- PyObject *ob = type->tp_alloc(type, 0);
- auto *self = reinterpret_cast<SbkVoidPtrObject *>(ob);
+ auto *self = PepExt_TypeCallAlloc<SbkVoidPtrObject>(type, 0);
if (self != nullptr) {
self->cptr = nullptr;
PyObject *toBytes(PyObject *self, PyObject * /* args */)
{
auto *sbkObject = reinterpret_cast<SbkVoidPtrObject *>(self);
- if (sbkObject->size < 0) {
- PyErr_SetString(PyExc_IndexError, "VoidPtr does not have a size set.");
- return nullptr;
- }
+ if (sbkObject->size < 0)
+ return PyErr_Format(PyExc_IndexError, "VoidPtr does not have a size set.");
+
PyObject *bytes = PyBytes_FromStringAndSize(reinterpret_cast<const char *>(sbkObject->cptr),
sbkObject->size);
Py_XINCREF(bytes);
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+# flake8: noqa F:821
+# flake8: noqa F:401
+
"""
__feature__.py (renamed to feature.py)
All these variables are transparently kept in module `builtins`.
"""
+
def feature_import(name, *args, **kwargs):
# PYSIDE-1368: The `__name__` attribute does not need to exist in all modules.
# PYSIDE-1398: sys._getframe(1) may not exist when embedding.
# Redirect to the original import
return None
+
_is_initialized = False
+
def __init__():
global _is_initialized
if not _is_initialized:
sys.modules["PySide6.QtCore"].__init_feature__()
return _current_selection(flag)
+
# The set_section(0) case seems to be unsafe. We will migrate to
# use the opaque feature.reset() call in all test cases.
def reset():
you are changing messages (what I did, of course :-) .
"""
-import os
import glob
from pathlib import Path
utf8_line = "# This Python file uses the following encoding: utf-8\n"
marker_line = f"# It has been edited by {Path(__file__).name} .\n"
+
def patch_file(fname):
with fname.open() as f:
lines = f.readlines()
with open(fname, "w") as f:
f.write("".join(lines))
+
def doit():
dirname = Path(__file__).parent
patched_files = []
print("Working on", fname)
patch_file(fname)
+
if __name__ == "__main__":
doit()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+# flake8: noqa E:721
+
"""
errorhandler.py
"""
import collections.abc
-import inspect
-import sys
import typing
from shibokensupport.signature import get_signature
-from shibokensupport.signature.mapping import update_mapping, namespace
+from shibokensupport.signature.mapping import namespace
from textwrap import dedent
if info == "<":
msg = f"{func_name}(): not enough arguments"
elif info == "0":
- msg = (f"{func_name}(): not enough arguments. "
- "Note: keyword arguments are only supported for optional parameters.")
+ msg = (f"{func_name}(): not enough arguments. "
+ "Note: keyword arguments are only supported for optional parameters.")
elif info == ">":
msg = f"{func_name}(): too many arguments"
elif info.isalnum():
except Exception as e:
name = e.__class__.__qualname__
print(72 * "*")
- print(f"Error in deprecated.py, ignored:")
+ print("Error in deprecated.py, ignored:")
print(f" {name}: {e}")
+
"""
A note for people who might think this could be written in pure Python:
gave up, since the solution is anyway not too nice when __import__ must
be overridden.
"""
-#eof
The only allowed values are '{allowed_values}'.
"""))
+
# The following names are used literally in this module.
# This way, we avoid the dict hashing problem.
signature = SignatureLayout()
P = inspect.Parameter
newname = "NamelessParameter"
bases = P.__bases__
- body = dict(P.__dict__) # get rid of mappingproxy
+ body = dict(P.__dict__) # get rid of mappingproxy
if "__slots__" in body:
# __slots__ would create duplicates
for name in body["__slots__"]:
signature.parameters[key].__class__ = NamelessParameter
-_POSITIONAL_ONLY = inspect._POSITIONAL_ONLY
-_POSITIONAL_OR_KEYWORD = inspect._POSITIONAL_OR_KEYWORD
-_VAR_POSITIONAL = inspect._VAR_POSITIONAL
-_KEYWORD_ONLY = inspect._KEYWORD_ONLY
-_VAR_KEYWORD = inspect._VAR_KEYWORD
-_empty = inspect._empty
+_POSITIONAL_ONLY = inspect._POSITIONAL_ONLY # noqa E:201
+_POSITIONAL_OR_KEYWORD = inspect._POSITIONAL_OR_KEYWORD # noqa E:201
+_VAR_POSITIONAL = inspect._VAR_POSITIONAL # noqa E:201
+_KEYWORD_ONLY = inspect._KEYWORD_ONLY # noqa E:201
+_VAR_KEYWORD = inspect._VAR_KEYWORD # noqa E:201
+_empty = inspect._empty # noqa E:201
+
def create_signature(props, key):
if not props:
return list(create_signature(elem, key)
for elem in props["multi"])
if type(key) is tuple:
- sig_kind, modifier = key
+ _, modifier = key
else:
- sig_kind, modifier = key, "signature"
+ _, modifier = key, "signature"
layout = globals()[modifier] # lookup of the modifier in this module
if not isinstance(layout, SignatureLayout):
if kind == _VAR_POSITIONAL:
kind = _KEYWORD_ONLY
sig = inspect.Signature(params,
- return_annotation=annotations.get('return', _empty),
- __validate_parameters__=False)
+ return_annotation=annotations.get('return', _empty),
+ __validate_parameters__=False)
# the special case of nameless parameters
if not layout.parameter_names:
import inspect
import sys
import types
-import typing
from shibokensupport.signature import get_signature as get_sig
-from shibokensupport.signature.layout import create_signature
"""
return ret
if "<" in class_name:
# This is happening in QtQuick for some reason:
- ## class std::shared_ptr<QQuickItemGrabResult >:
+ # class std::shared_ptr<QQuickItemGrabResult >:
# We simply skip over this class.
return ret
bases_list = []
enums = []
properties = []
signals = []
+ attributes = {}
for thing_name, thing in class_members:
if signal_check(thing):
enums.append((thing_name, type(thing).__qualname__, thing))
elif isinstance(thing, property):
properties.append((thing_name, thing))
+ # Support attributes that have PySide types as values,
+ # but we skip the 'staticMetaObject' that needs
+ # to be defined at a QObject level.
+ elif "PySide" in str(type(thing)) and "QMetaObject" not in str(type(thing)):
+ if class_name not in attributes:
+ attributes[class_name] = {}
+ attributes[class_name][thing_name] = thing
if thing_name in self.collision_candidates:
self.collision_track.add(thing_name)
# find out how many functions create a signature
sigs = list(_ for _ in functions if get_sig(_[1]))
- self.fmt.have_body = bool(subclasses or sigs or properties or enums or init_signature
- or signals)
+ self.fmt.have_body = bool(subclasses or sigs or properties or enums or # noqa W:504
+ init_signature or signals or attributes)
with self.fmt.klass(class_name, class_str):
self.fmt.level += 1
if len(enums):
self.section()
for enum_name, enum_class_name, value in enums:
- with self.fmt.enum(enum_class_name, enum_name,value.value):
+ with self.fmt.enum(enum_class_name, enum_name, value.value):
pass
if hasattr(self.fmt, "signal"):
# this is an optional feature
sig_str = str(signal)
with self.fmt.signal(sig_class_name, signal_name, sig_str):
pass
+ if hasattr(self.fmt, "attribute"):
+ if len(attributes):
+ self.section()
+ for class_name, attrs in attributes.items():
+ for attr_name, attr_value in attrs.items():
+ with self.fmt.attribute(attr_name, attr_value):
+ pass
if len(subclasses):
self.section()
for subclass_name, subclass in subclasses:
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
"""
+# flake8: noqa E:402
+
"""
pyi_generator.py
from contextlib import contextmanager
from textwrap import dedent
-from shiboken6 import Shiboken
from shibokensupport.signature.lib.enum_sig import HintingEnumerator
from shibokensupport.signature.lib.tool import build_brace_pattern
pattern = fr"\b Union \s* \[ \s* {brace_pat} \s*, \s* NoneType \s* \]"
replace = r"Optional[\1]"
optional_searcher = re.compile(pattern, flags=re.VERBOSE)
+
def optional_replacer(source):
return optional_searcher.sub(replace, str(source))
self.optional_replacer = optional_replacer
self.print(f"{spaces}{enum_name:25}: {class_name} = ... # {hexval}")
yield
+ @contextmanager
+ def attribute(self, attr_name, attr_value):
+ spaces = indent * self.level
+ self.print(f"{spaces}{attr_name:25} = ... # type: {type(attr_value).__qualname__}")
+ yield
+
@contextmanager
def signal(self, class_name, sig_name, sig_str):
spaces = indent * self.level
(None, ["builtins"]),
(None, ["os"]),
(None, ["enum"]),
+ ("collections.abc", ["Iterable"]),
("typing", sorted(typing.__all__)),
("PySide6.QtCore", ["PyClassProperty", "Signal", "SignalInstance"]),
("shiboken6", ["Shiboken"]),
]
+
def filter_from_imports(from_struct, text):
"""
Build a reduced new `from` structure (nfs) with found entries, only
if (f"class {each}(") not in text:
if re.search(rf"(\b|@){each}\b([^\s\(:]|\n)", text):
lis.append(each)
+ # Search if a type is present in the return statement
+ # of function declarations: '... -> here:'
+ if re.search(rf"->.*{each}.*:", text):
+ lis.append(each)
if not lis:
nfs.pop()
return nfs
obj = getattr(top, plainname) if import_name != plainname else top
if not getattr(obj, "__file__", None) or Path(obj.__file__).is_dir():
raise ModuleNotFoundError(f"We do not accept a namespace as module `{plainname}`")
- module = sys.modules[import_name]
outfile = io.StringIO()
fmt = Formatter(outfile, options)
fmt.print(LICENSE_TEXT.strip())
- need_imports = options._pyside_call and not USE_PEP563
if USE_PEP563:
fmt.print("from __future__ import annotations")
fmt.print()
wr.print(f"from {mod} import {import_args}")
wr.print()
wr.print()
- wr.print("NoneType = type(None)")
+ wr.print("NoneType: TypeAlias = type[None]")
wr.print()
else:
wr.print(line)
if not options.quiet:
options.logger.info(f"Generated: {outfilepath}")
- # PYSIDE-1735: .pyi files are no longer compatible with Python, because
- # enum classes contain ellipsis which a Python enum forbids.
- # We will implement tests with Mypy, instead.
def main():
pyi_generator will try to generate an interface "<module>.pyi".
"""))
parser.add_argument("module",
- help="The full path name of an importable module binary (.pyd, .so)")
+ help="The full path name of an importable module binary (.pyd, .so)") # noqa E:128
parser.add_argument("--quiet", action="store_true", help="Run quietly")
- parser.add_argument("--check", action="store_true", help="Test the output")
parser.add_argument("--outpath",
- help="the output directory (default = location of module binary)")
+ help="the output directory (default = location of module binary)") # noqa E:128
options = parser.parse_args()
module = options.module
outpath = options.outpath
options.logger = logger
generate_pyi(module, outpath, options=options)
+
if __name__ == "__main__":
main()
# eof
ro, rc = round_ = "()"
so, sc = square = "[]"
- co, cc = curly = "CD" # we insert "{}", later...
- ao, ac = angle = "<>"
+ co, cc = curly = "CD" # noqa E:201 we insert "{}", later...
+ ao, ac = angle = "<>" # noqa E:201
q2, bs, q1 = '"', "\\", "'"
allpat = round_ + square + curly + angle
__ = " "
{indent} )*
""")
for idx in range(level):
- pattern = pattern.format(replacer = repeated if idx < level-1 else no_braces_q,
- indent = idx * " ", **locals())
+ pattern = pattern.format(replacer=repeated if idx < level - 1 else no_braces_q,
+ indent=idx * " ", **locals())
return pattern.replace("C", "{").replace("D", "}")
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+# flake8: noqa E:402
+# flake8: noqa F:401
+
"""
loader.py
def pyside_type_init(type_key, sig_strings):
return parser.pyside_type_init(type_key, sig_strings)
+
# name used in signature.cpp
def create_signature(props, key):
return layout.create_signature(props, key)
+
# name used in signature.cpp
def seterror_argument(args, func_name, info):
return errorhandler.seterror_argument(args, func_name, info)
+
# name used in signature.cpp
def make_helptext(func):
return errorhandler.make_helptext(func)
+
# name used in signature.cpp
def finish_import(module):
return importhandler.finish_import(module)
+
# name used in signature.cpp
def feature_import(*args, **kwds):
# don't spend a stack level here for speed and compatibility
feature_import = feature.feature_import
return feature_import(*args, **kwds)
+
# name used in signature.cpp
def feature_imported(*args, **kwds):
# don't spend a stack level here for speed and compatibility
put_into_package(PySide6.support.signature.lib, pyi_generator)
put_into_package(PySide6.support.signature.lib, tool)
+
from shibokensupport.signature import mapping
from shibokensupport.signature import errorhandler
from shibokensupport.signature import layout
import enum
-post_init = lambda:None # default
+post_init = lambda: None # noqa E:731 default
if "PySide6" in sys.modules:
# We publish everything under "PySide6.support", again.
# PYSIDE-1502: Make sure that support can be imported.
try:
import PySide6.support
- except ModuleNotFoundError as e:
+ except ModuleNotFoundError:
print("PySide6.support could not be imported. "
"This is a serious configuration error.", file=sys.stderr)
raise
"PyCallable": typing.Callable,
"PyObject": object,
"PyObject*": object,
- "PyArrayObject": ArrayLikeVariable, # numpy
- "PyPathLike": typing.Union[str, bytes, os.PathLike],
- "PySequence": typing.Iterable, # important for numpy
+ "PyArrayObject": ArrayLikeVariable, # numpy
+ "PyPathLike": typing.Union[str, bytes, os.PathLike[str]],
+ "PySequence": typing.Iterable, # important for numpy
"PyTypeObject": type,
"QChar": str,
"QHash": typing.Dict,
"qreal": float,
"QSet": typing.Set,
"QString": str,
+ "QLatin1String": str,
"QStringView": str,
"QStringList": StringList,
"quint16": int,
"QVariant()": Invalid(Variant),
"QVariant.Type": type, # not so sure here...
"QVariantMap": typing.Dict[str, Variant],
+ "std.chrono.seconds{5}" : ellipsis,
})
try:
type_map.update({
"int32_t": int,
"HBITMAP": int,
"HICON": int,
+ "HMONITOR": int,
"HRGN": int,
"QPixmap()": Default("PySide6.QtGui.QPixmap"), # can't create without qApp
"QPlatformSurface*": int, # a handle
import ast
import enum
-import functools
import keyword
import os
import re
import sys
-import types
import typing
import warnings
from types import SimpleNamespace
from shibokensupport.signature.mapping import (type_map, update_mapping,
- namespace, _NotCalled, ResultVariable, ArrayLikeVariable)
+ namespace, _NotCalled, ResultVariable, ArrayLikeVariable) # noqa E:128
from shibokensupport.signature.lib.tool import build_brace_pattern
-from shibokensupport import feature
_DEBUG = False
LIST_KEYWORDS = False
In effect, 'type_map' maps text to real Python objects.
"""
+
def _get_flag_enum_option():
- from shiboken6 import (__version_info__ as ver,
+ from shiboken6 import (__version_info__ as ver, # noqa F:401
__minimum_python_version__ as pyminver,
__maximum_python_version__ as pymaxver)
# PYSIDE-1735: Emit a warning when we should maybe evict forgiveness mode
if ver[:2] >= (7, 0):
warnings.warn(f"{q} Please drop Enum forgiveness mode in `mangled_type_getattro` ***")
+ # PYSIDE-2404: Emit a warning when we should drop uppercase offset constants
+ if ver[:2] >= (7, 0):
+ warnings.warn(f"{q} Please drop uppercase type offsets in `copyOffsetEnumStream` ***")
# normalize the sys attribute
setattr(sys, sysname, flag)
os.environ[envname] = str(flag)
_cache = {}
+
def _parse_arglist(argstr):
# The following is a split re. The string is broken into pieces which are
# between the recognized strings. Because the re has groups, both the
if not match:
return thing
start, stop = match.start(), match.end() - 1
- pre, func, args = thing[:start], thing[start : stop], thing[stop:]
+ pre, func, args = thing[:start], thing[start:stop], thing[stop:]
if func[0].isupper() or func.startswith("gl") and func[2:3].isupper():
return thing
# Now convert this string to snake case.
if char.isupper():
if idx and func[idx - 1].isupper():
# two upper chars are forbidden
- return things
+ return thing
snake_func += f"_{char.lower()}"
else:
snake_func += char
return ret
return None
+
def get_name(thing):
if isinstance(thing, type):
return getattr(thing, "__qualname__", thing.__name__)
else:
return thing.__name__
+
def _resolve_value(thing, valtype, line):
if thing in ("0", "None") and valtype:
if valtype.startswith("PySide6.") or valtype.startswith("typing."):
dot = "." in str(thing) or m not in (thing.__qualname__, "builtins")
name = get_name(thing)
ret = m + "." + name if dot else name
- assert(eval(ret, globals(), namespace))
+ assert (eval(ret, globals(), namespace))
return ret
# Note: This captures things from the typing module:
return str(thing)
matrix_pattern = "PySide6.QtGui.QGenericMatrix"
+
def handle_matrix(arg):
- n, m, typstr = tuple(map(lambda x:x.strip(), arg.split(",")))
+ n, m, typstr = tuple(map(lambda x: x.strip(), arg.split(",")))
assert typstr == "float"
result = f"PySide6.QtGui.QMatrix{n}x{m}"
return eval(result, globals(), namespace)
# Special case: Handle the generic matrices.
if contr == matrix_pattern:
return handle_matrix(thing)
- contr = var_handler(_resolve_type(contr, line, level+1, var_handler))
+ contr = var_handler(_resolve_type(contr, line, level + 1, var_handler))
if isinstance(contr, _NotCalled):
raise SystemError("Container types must exist:", repr(contr))
contr = to_string(contr)
pieces = []
for part in _parse_arglist(thing):
- part = var_handler(_resolve_type(part, line, level+1, var_handler))
+ part = var_handler(_resolve_type(part, line, level + 1, var_handler))
if isinstance(part, _NotCalled):
# fix the tag (i.e. "Missing") by repr
part = repr(part)
# PYSIDE-1538: Make sure that the eval does not crash.
try:
return eval(result, globals(), namespace)
- except Exception as e:
+ except Exception:
warnings.warn(f"""pyside_type_init:_resolve_type
UNRECOGNIZED: {result!r}
props.defaults = defaults
props.kwdefaults = {}
props.annotations = annotations
- props.varnames = varnames = tuple(tup[0] for tup in arglist)
+ props.varnames = tuple(tup[0] for tup in arglist)
funcname = parsed.funcname
- shortname = funcname[funcname.rindex(".")+1:]
+ shortname = funcname[funcname.rindex(".") + 1:]
props.name = shortname
props.multi = parsed.multi
fix_variables(props, line)
else:
diff -= 1
if retvars:
- rvs = []
retvars = list(handle_retvar(rv) if isinstance(rv, ArrayLikeVariable) else rv
for rv in retvars)
if len(retvars) == 1:
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+// @snippet isvalid
+bool isValid = Shiboken::Object::isValid(%1, false);
+%PYARG_0 = %CONVERTTOPYTHON[bool](isValid);
+// @snippet isvalid
+
+// @snippet wrapinstance
+auto *pyType = reinterpret_cast<PyTypeObject *>(%2);
+if (Shiboken::ObjectType::checkType(pyType)) {
+ auto *ptr = reinterpret_cast<void *>(%1);
+ if (auto *wrapper = Shiboken::BindingManager::instance().retrieveWrapper(ptr)) {
+ Py_INCREF(wrapper);
+ %PYARG_0 = reinterpret_cast<PyObject *>(wrapper);
+ } else {
+ %PYARG_0 = Shiboken::Object::newObject(pyType, ptr, false, true);
+ }
+} else {
+ PyErr_SetString(PyExc_TypeError, "You need a shiboken-based type.");
+}
+// @snippet wrapinstance
+
+// @snippet getcpppointer
+if (Shiboken::Object::checkType(%1)) {
+ std::vector<void*> ptrs = Shiboken::Object::cppPointers(reinterpret_cast<SbkObject *>(%1));
+ %PYARG_0 = PyTuple_New(ptrs.size());
+ for (std::size_t i = 0; i < ptrs.size(); ++i)
+ PyTuple_SET_ITEM(%PYARG_0, i, PyLong_FromVoidPtr(ptrs[i]));
+} else {
+ PyErr_SetString(PyExc_TypeError, "You need a shiboken-based type.");
+}
+// @snippet getcpppointer
+
+// @snippet delete
+if (Shiboken::Object::checkType(%1)) {
+ Shiboken::Object::callCppDestructors(reinterpret_cast<SbkObject *>(%1));
+} else {
+ PyErr_SetString(PyExc_TypeError, "You need a shiboken-based type.");
+}
+// @snippet delete
+
+// @snippet ownedbypython
+if (Shiboken::Object::checkType(%1)) {
+ bool hasOwnership = Shiboken::Object::hasOwnership(reinterpret_cast<SbkObject *>(%1));
+ %PYARG_0 = %CONVERTTOPYTHON[bool](hasOwnership);
+} else {
+ PyErr_SetString(PyExc_TypeError, "You need a shiboken-based type.");
+}
+// @snippet ownedbypython
+
+// @snippet createdbypython
+if (Shiboken::Object::checkType(%1)) {
+ bool wasCreatedByPython = Shiboken::Object::wasCreatedByPython(reinterpret_cast<SbkObject *>(%1));
+ %PYARG_0 = %CONVERTTOPYTHON[bool](wasCreatedByPython);
+} else {
+ PyErr_SetString(PyExc_TypeError, "You need a shiboken-based type.");
+}
+// @snippet createdbypython
+
+// @snippet disassembleframe
+Shiboken::AutoDecRef label(PyObject_Str(%1));
+const char *marker = Shiboken::String::toCString(label);
+disassembleFrame(marker);
+Py_INCREF(Py_None);
+%PYARG_0 = Py_None;
+// @snippet disassembleframe
+
+// @snippet dump
+if (!Shiboken::Object::checkType(%1)) {
+ %PYARG_0 = Shiboken::String::fromCString("Ordinary Python type.");
+} else {
+ std::string str = Shiboken::Object::info(reinterpret_cast<SbkObject *>(%1));
+ %PYARG_0 = Shiboken::String::fromCString(str.c_str());
+}
+// @snippet dump
+
+// @snippet getallvalidwrappers
+const auto setAll = Shiboken::BindingManager::instance().getAllPyObjects();
+PyObject* listAll = PyList_New(0);
+if (listAll == nullptr)
+ return nullptr;
+for (auto *o : setAll) {
+ if (o != nullptr) {
+ if (PyList_Append(listAll, o) != 0) {
+ Py_DECREF(listAll);
+ return nullptr;
+ }
+ }
+}
+return listAll;
+// @snippet getallvalidwrappers
+
+// @snippet dumptypegraph
+const bool ok = Shiboken::BindingManager::instance().dumpTypeGraph(%1);
+%PYARG_0 = %CONVERTTOPYTHON[bool](ok);
+// @snippet dumptypegraph
+
+// @snippet dumpwrappermap
+Shiboken::BindingManager::instance().dumpWrapperMap();
+// @snippet dumpwrappermap
+
+// @snippet init
+// Add __version__ and __version_info__ attributes to the module
+PyObject* version = PyTuple_New(5);
+PyTuple_SET_ITEM(version, 0, PyLong_FromLong(SHIBOKEN_MAJOR_VERSION));
+PyTuple_SET_ITEM(version, 1, PyLong_FromLong(SHIBOKEN_MINOR_VERSION));
+PyTuple_SET_ITEM(version, 2, PyLong_FromLong(SHIBOKEN_MICRO_VERSION));
+PyTuple_SET_ITEM(version, 3, Shiboken::String::fromCString(SHIBOKEN_RELEASE_LEVEL));
+PyTuple_SET_ITEM(version, 4, PyLong_FromLong(SHIBOKEN_SERIAL));
+PyModule_AddObject(module, "__version_info__", version);
+PyModule_AddStringConstant(module, "__version__", SHIBOKEN_VERSION);
+VoidPtr::addVoidPtrToModule(module);
+Shiboken::initShibokenSupport(module);
+// @snippet init
<?xml version="1.0" ?>
+<!--
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+-->
<typesystem package="Shiboken">
<primitive-type name="bool" />
<primitive-type name="unsigned long" />
<primitive-type name="size_t" />
<add-function signature="isValid(PyObject*)" return-type="bool">
- <inject-code>
- bool isValid = Shiboken::Object::isValid(%1, false);
- %PYARG_0 = %CONVERTTOPYTHON[bool](isValid);
- </inject-code>
+ <inject-code file="shibokenmodule.cpp" snippet="isvalid"/>
</add-function>
<add-function signature="invalidate(PyObject*)">
</add-function>
<add-function signature="wrapInstance(size_t, PyTypeObject)" return-type="PyObject*">
- <inject-code>
- auto *pyType = reinterpret_cast<PyTypeObject *>(%2);
- if (Shiboken::ObjectType::checkType(pyType)) {
- %PYARG_0 = Shiboken::Object::newObject(pyType,
- reinterpret_cast<void *>(%1),
- false, true);
- } else {
- PyErr_SetString(PyExc_TypeError, "You need a shiboken-based type.");
- }
- </inject-code>
+ <inject-code file="shibokenmodule.cpp" snippet="wrapinstance"/>
</add-function>
<add-function signature="getCppPointer(PyObject*)" return-type="PySequence*">
- <inject-code>
- if (Shiboken::Object::checkType(%1)) {
- std::vector<void*> ptrs = Shiboken::Object::cppPointers(reinterpret_cast<SbkObject *>(%1));
- %PYARG_0 = PyTuple_New(ptrs.size());
- for (std::size_t i = 0; i < ptrs.size(); ++i)
- PyTuple_SET_ITEM(%PYARG_0, i, PyLong_FromVoidPtr(ptrs[i]));
- } else {
- PyErr_SetString(PyExc_TypeError, "You need a shiboken-based type.");
- }
- </inject-code>
+ <inject-code file="shibokenmodule.cpp" snippet="getcpppointer"/>
</add-function>
<add-function signature="delete(PyObject*)">
- <inject-code>
- if (Shiboken::Object::checkType(%1)) {
- Shiboken::Object::callCppDestructors(reinterpret_cast<SbkObject *>(%1));
- } else {
- PyErr_SetString(PyExc_TypeError, "You need a shiboken-based type.");
- }
- </inject-code>
+ <inject-code file="shibokenmodule.cpp" snippet="delete"/>
</add-function>
<add-function signature="ownedByPython(PyObject*)" return-type="bool">
- <inject-code>
- if (Shiboken::Object::checkType(%1)) {
- bool hasOwnership = Shiboken::Object::hasOwnership(reinterpret_cast<SbkObject *>(%1));
- %PYARG_0 = %CONVERTTOPYTHON[bool](hasOwnership);
- } else {
- PyErr_SetString(PyExc_TypeError, "You need a shiboken-based type.");
- }
- </inject-code>
+ <inject-code file="shibokenmodule.cpp" snippet="ownedbypython"/>
</add-function>
<add-function signature="createdByPython(PyObject*)" return-type="bool">
- <inject-code>
- if (Shiboken::Object::checkType(%1)) {
- bool wasCreatedByPython = Shiboken::Object::wasCreatedByPython(reinterpret_cast<SbkObject *>(%1));
- %PYARG_0 = %CONVERTTOPYTHON[bool](wasCreatedByPython);
- } else {
- PyErr_SetString(PyExc_TypeError, "You need a shiboken-based type.");
- }
- </inject-code>
+ <inject-code file="shibokenmodule.cpp" snippet="createdbypython"/>
</add-function>
<add-function signature="disassembleFrame(PyObject*)" return-type="PyObject">
- <inject-code>
- Shiboken::AutoDecRef label(PyObject_Str(%1));
- const char *marker = Shiboken::String::toCString(label);
- disassembleFrame(marker);
- Py_INCREF(Py_None);
- %PYARG_0 = Py_None;
- </inject-code>
+ <inject-code file="shibokenmodule.cpp" snippet="disassembleframe"/>
</add-function>
<add-function signature="dump(PyObject*)" return-type="const char *">
- <inject-code>
- if (!Shiboken::Object::checkType(%1)) {
- %PYARG_0 = Shiboken::String::fromCString("Ordinary Python type.");
- } else {
- std::string str = Shiboken::Object::info(reinterpret_cast<SbkObject *>(%1));
- %PYARG_0 = Shiboken::String::fromCString(str.c_str());
- }
- </inject-code>
+ <inject-code file="shibokenmodule.cpp" snippet="dump"/>
</add-function>
<add-function signature="getAllValidWrappers(void)" return-type="PySequence*">
- <inject-code>
- const auto setAll = Shiboken::BindingManager::instance().getAllPyObjects();
- PyObject* listAll = PyList_New(0);
- if (listAll == nullptr)
- return nullptr;
+ <inject-code file="shibokenmodule.cpp" snippet="getallvalidwrappers"/>
+ </add-function>
- for (auto *o : setAll) {
- if (o != nullptr) {
- if (PyList_Append(listAll, o) != 0) {
- Py_DECREF(listAll);
- return nullptr;
- }
- }
- }
- return listAll;
- </inject-code>
+ <add-function signature="dumpTypeGraph(const char *@fileName@)" return-type="bool">
+ <inject-code file="shibokenmodule.cpp" snippet="dumptypegraph"/>
+ </add-function>
+
+ <add-function signature="dumpWrapperMap()">
+ <inject-code file="shibokenmodule.cpp" snippet="dumpwrappermap"/>
</add-function>
<extra-includes>
<include file-name="sbkversion.h" location="local"/>
<include file-name="voidptr.h" location="local"/>
</extra-includes>
- <inject-code position="end">
- // Add __version__ and __version_info__ attributes to the module
- PyObject* version = PyTuple_New(5);
- PyTuple_SET_ITEM(version, 0, PyLong_FromLong(SHIBOKEN_MAJOR_VERSION));
- PyTuple_SET_ITEM(version, 1, PyLong_FromLong(SHIBOKEN_MINOR_VERSION));
- PyTuple_SET_ITEM(version, 2, PyLong_FromLong(SHIBOKEN_MICRO_VERSION));
- PyTuple_SET_ITEM(version, 3, Shiboken::String::fromCString(SHIBOKEN_RELEASE_LEVEL));
- PyTuple_SET_ITEM(version, 4, PyLong_FromLong(SHIBOKEN_SERIAL));
- PyModule_AddObject(module, "__version_info__", version);
- PyModule_AddStringConstant(module, "__version__", SHIBOKEN_VERSION);
- VoidPtr::addVoidPtrToModule(module);
-
- Shiboken::initShibokenSupport(module);
- </inject-code>
+ <inject-code position="end" file="shibokenmodule.cpp" snippet="init"/>
</typesystem>
"float", "double"
};
-static inline QString nameAttribute() { return QStringLiteral("name"); }
+constexpr auto nameAttribute = "name"_L1;
static void formatXmlClass(QXmlStreamWriter &writer, const ClassModelItem &klass);
static void formatXmlEnum(QXmlStreamWriter &writer, const EnumModelItem &en)
{
writer.writeStartElement(u"enum-type"_s);
- writer.writeAttribute(nameAttribute(), en->name());
+ writer.writeAttribute(nameAttribute, en->name());
writer.writeEndElement();
}
formatXmlLocationComment(writer, klass);
writer.writeStartElement(isValueType ? u"value-type"_s
: u"object-type"_s);
- writer.writeAttribute(nameAttribute(), klass->name());
+ writer.writeAttribute(nameAttribute, klass->name());
formatXmlScopeMembers(writer, klass);
writer.writeEndElement();
}
{
formatXmlLocationComment(writer, nsp);
writer.writeStartElement(u"namespace-type"_s);
- writer.writeAttribute(nameAttribute(), nsp->name());
+ writer.writeAttribute(nameAttribute, nsp->name());
}
static void formatXmlNamespaceMembers(QXmlStreamWriter &writer, const NamespaceModelItem &nsp)
QDateTime::currentDateTime().toString(Qt::ISODate));
for (auto p : primitiveTypes) {
writer.writeStartElement(u"primitive-type"_s);
- writer.writeAttribute(nameAttribute(), QLatin1StringView(p));
+ writer.writeAttribute(nameAttribute, QLatin1StringView(p));
writer.writeEndElement();
}
formatXmlNamespaceMembers(writer, dom);
#include "libothermacros.h"
#include "multiple_derived.h"
+#include "objecttype.h"
#include "virtualmethods.h"
class ObjectType;
std::cout << '>';
}
+void Abstract::virtualWithOutParameter(int &x) const
+{
+ x = 42;
+}
+
+int Abstract::callVirtualWithOutParameter() const
+{
+ int x;
+ virtualWithOutParameter(x);
+ return x;
+}
+
void Abstract::callVirtualGettingEnum(PrintFormat p)
{
virtualGettingAEnum(p);
virtual void hideFunction(HideType *arg) = 0;
+ virtual void virtualWithOutParameter(int &x) const;
+ int callVirtualWithOutParameter() const;
+
protected:
virtual const char *className() const { return "Abstract"; }
public:
void uselessMethod() {}
SomeInnerClass operator+(const SomeInnerClass &other) { return other; }
- bool operator==(const SomeInnerClass &) { return true; }
+ bool operator==(const SomeInnerClass &) const { return true; }
};
explicit Derived(int id = -1) noexcept;
std::cout << "(x: " << m_x << ", y: " << m_y << ")";
}
-bool Point::operator==(const Point &other)
+bool Point::operator==(const Point &other) const
{
return m_x == other.m_x && m_y == other.m_y;
}
// The != operator is not implemented for the purpose of testing
// for the absense of the __ne__ method in the Python binding.
- bool operator==(const Point &other);
+ bool operator==(const Point &other) const;
Point operator+(const Point &other);
Point operator-(const Point &other);
std::cout << "(x: " << m_x << ", y: " << m_y << ")";
}
-bool PointF::operator==(const PointF &other)
+bool PointF::operator==(const PointF &other) const
{
return m_x == other.m_x && m_y == other.m_y;
}
// The != operator is not implemented for the purpose of testing
// for the absence of the __ne__ method in the Python binding.
- bool operator==(const PointF &other);
+ bool operator==(const PointF &other) const;
PointF operator+(const PointF &other);
PointF operator-(const PointF &other);
std::cerr << '\n';
}
+std::shared_ptr<std::string> StdSharedPtrTestBench::createString(const char *text)
+{
+ return std::make_shared<std::string>(text);
+}
+
+std::shared_ptr<std::string> StdSharedPtrTestBench::createNullString()
+{
+ return {};
+}
+
+void StdSharedPtrTestBench::printString(const std::shared_ptr<std::string> &p)
+{
+ std::cerr << __FUNCTION__ << ' ';
+ if (p.get())
+ std::cerr << '"' << *p << '"';
+ else
+ std::cerr << "nullptr";
+ std::cerr << '\n';
+}
+
StdSharedPtrVirtualMethodTester::StdSharedPtrVirtualMethodTester() = default;
StdSharedPtrVirtualMethodTester::~StdSharedPtrVirtualMethodTester() = default;
#include "libsmartmacros.h"
#include <memory>
+#include <string>
class Integer;
static std::shared_ptr<int> createInt(int v = 42);
static std::shared_ptr<int> createNullInt();
static void printInt(const std::shared_ptr<int> &);
+
+ static std::shared_ptr<std::string> createString(const char *text);
+ static std::shared_ptr<std::string> createNullString();
+ static void printString(const std::shared_ptr<std::string> &);
};
class LIB_SMART_API StdSharedPtrVirtualMethodTester
from shiboken_paths import init_paths
init_paths()
-from shiboken6 import Shiboken
+from shiboken6 import Shiboken # noqa: F401
from shibokensupport.signature.lib.tool import build_brace_pattern
The pattern is crucial, because it is used heavily in signature.parser .
"""
-# A slow reference parser for braces and strings
+
def check(s):
+ """A slow reference parser for braces and strings"""
open, close = "[{(<", "]})>"
escape, quote = "\\", '"'
instring = blind = False
stack.append(c)
elif c in close:
pos = close.index(c)
- if ((len(stack) > 0) and
- (open[pos] == stack[len(stack)-1])):
+ if len(stack) > 0 and open[pos] == stack[len(stack) - 1]:
stack.pop()
else:
return False
return [not mb1, not mb2]
def oredMinBoolList(self, minBoolList):
- return not reduce(lambda a, b: a|b, minBoolList)
+ return not reduce(lambda a, b: a | b, minBoolList)
def createValList(self, num):
return [Val(i) for i in range(0, num * 2, 2)]
def testSumListOfIntListsFromExtendedClass(self):
lu = ExtListUser()
lst = [range(4)] * 4
- self.assertEqual(lu.sumListOfIntLists(lst), sum([sum(line) for line in [range(4)] * 4]) * 2)
- self.assertEqual(lu.callSumListOfIntLists(lst), sum([sum(line) for line in [range(4)] * 4]) * 2)
+ self.assertEqual(lu.sumListOfIntLists(lst),
+ sum([sum(line) for line in [range(4)] * 4]) * 2)
+ self.assertEqual(lu.callSumListOfIntLists(lst),
+ sum([sum(line) for line in [range(4)] * 4]) * 2)
def testOpaqueContainer(self):
lu = ListUser()
# Set via Python
- python_list = [1,2]
+ python_list = [1, 2]
lu.setStdIntList(python_list)
self.assertEqual(len(lu.m_stdIntList), 2)
self.assertEqual(lu.m_stdIntList[0], 1)
self.assertEqual(lu.m_stdIntList[2], 5)
# Access list via getter
- l = lu.getIntList()
- l.append(6)
+ il = lu.getIntList()
+ il.append(6)
self.assertEqual(len(lu.m_stdIntList), 4)
self.assertEqual(lu.m_stdIntList[3], 6)
from minimal import MinBoolUser
+
class DerivedMinBoolUser (MinBoolUser):
def returnMyselfVirtual(self):
return MinBoolUser()
+
class MinBoolTest(unittest.TestCase):
def testMinBoolUser(self):
mbuTrue = MinBoolUser()
mbuFalse = MinBoolUser()
mbuTrue.setMinBool(True)
- self.assertEqual(mbuFalse.minBool(), False)
- self.assertEqual(mbuTrue.minBool(), True)
- self.assertEqual(mbuTrue.callInvertedMinBool(), False)
+ self.assertFalse(mbuFalse.minBool())
+ self.assertTrue(mbuTrue.minBool())
+ self.assertFalse(mbuTrue.callInvertedMinBool())
- self.assertEqual(mbuTrue.minBool() == True, True)
- self.assertEqual(False == mbuFalse.minBool(), True)
- self.assertEqual(mbuTrue.minBool() == mbuFalse.minBool(), False)
+ self.assertTrue(mbuTrue.minBool())
+ self.assertFalse(mbuFalse.minBool())
+ self.assertTrue(mbuTrue.minBool() != mbuFalse.minBool())
- self.assertEqual(mbuFalse.minBool() != True, True)
- self.assertEqual(True != mbuFalse.minBool(), True)
- self.assertEqual(mbuTrue.minBool() != mbuFalse.minBool(), True)
+ self.assertFalse(mbuFalse.minBool())
+ self.assertFalse(mbuFalse.minBool())
+ self.assertTrue(mbuTrue.minBool() != mbuFalse.minBool())
def testVirtuals(self):
dmbu = DerivedMinBoolUser()
self.assertEqual(dmbu.invertedMinBool(), True)
+
if __name__ == '__main__':
unittest.main()
-
init_paths()
from minimal import Obj
+
class ExtObj(Obj):
def __init__(self, objId):
Obj.__init__(self, objId)
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-from functools import reduce
import os
import sys
import unittest
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
from shiboken_paths import init_paths
init_paths()
-from minimal import *
+from minimal import (arrayFunc, arrayFuncInt, arrayFuncIntReturn,
+ arrayFuncIntReturnTypedef, arrayFuncIntTypedef,
+ arrayFuncReturn, arrayFuncReturnTypedef, arrayFuncTypedef)
try:
import numpy as np
self.assertTrue(arrayFuncInt(none), "None is empty, arrayFuncInt should return true")
self.assertFalse(arrayFuncInt(full), "Full is NOT empty, arrayFuncInt should return false")
- self.assertTrue(arrayFuncInt(np.array(none)), "None is empty, arrayFuncInt should return true")
- self.assertFalse(arrayFuncInt(np.array(full)), "Full is NOT empty, arrayFuncInt should return false")
+ self.assertTrue(arrayFuncInt(np.array(none)),
+ "None is empty, arrayFuncInt should return true")
+ self.assertFalse(arrayFuncInt(np.array(full)),
+ "Full is NOT empty, arrayFuncInt should return false")
def test_arrayFuncIntTypedef(self):
none = ()
full = (1, 2, 3)
- self.assertTrue(arrayFuncIntTypedef(none), "None is empty, arrayFuncIntTypedef should return true")
- self.assertFalse(arrayFuncIntTypedef(full), "Full is NOT empty, arrayFuncIntTypedef should return false")
+ self.assertTrue(arrayFuncIntTypedef(none),
+ "None is empty, arrayFuncIntTypedef should return true")
+ self.assertFalse(arrayFuncIntTypedef(full),
+ "Full is NOT empty, arrayFuncIntTypedef should return false")
- self.assertTrue(arrayFuncIntTypedef(np.array(none)), "None is empty, arrayFuncIntTypedef should return true")
- self.assertFalse(arrayFuncIntTypedef(np.array(full)), "Full is NOT empty, arrayFuncIntTypedef should return false")
+ self.assertTrue(arrayFuncIntTypedef(np.array(none)),
+ "None is empty, arrayFuncIntTypedef should return true")
+ self.assertFalse(arrayFuncIntTypedef(np.array(full)),
+ "Full is NOT empty, arrayFuncIntTypedef should return false")
def test_arrayFuncIntReturn(self):
none = arrayFuncIntReturn(0)
full = arrayFuncIntReturn(self.the_size)
self.assertTrue((len(none) == 0), "none should be empty")
- self.assertTrue((len(full) == self.the_size), "full should have " + str(self.the_size) + " elements")
+ self.assertTrue((len(full) == self.the_size),
+ f"full should have {self.the_size} elements")
def test_arrayFuncIntReturnTypedef(self):
none = arrayFuncIntReturnTypedef(0)
full = arrayFuncIntReturnTypedef(self.the_size)
self.assertTrue((len(none) == 0), "none should be empty")
- self.assertTrue((len(full) == self.the_size), "full should have " + str(self.the_size) + " elements")
+ self.assertTrue((len(full) == self.the_size),
+ f"full should have {self.the_size} elements")
def test_arrayFunc(self):
none = ()
self.assertFalse(arrayFunc(full), "Full is NOT empty, arrayFunc should return false")
self.assertTrue(arrayFunc(np.array(none)), "None is empty, arrayFunc should return true")
- self.assertFalse(arrayFunc(np.array(full)), "Full is NOT empty, arrayFunc should return false")
+ self.assertFalse(arrayFunc(np.array(full)),
+ "Full is NOT empty, arrayFunc should return false")
def test_arrayFuncTypedef(self):
none = ()
full = (1, 2, 3)
- self.assertTrue(arrayFuncTypedef(none), "None is empty, arrayFuncTypedef should return true")
- self.assertFalse(arrayFuncTypedef(full), "Full is NOT empty, arrayFuncTypedef should return false")
+ self.assertTrue(arrayFuncTypedef(none),
+ "None is empty, arrayFuncTypedef should return true")
+ self.assertFalse(arrayFuncTypedef(full),
+ "Full is NOT empty, arrayFuncTypedef should return false")
- self.assertTrue(arrayFuncTypedef(np.array(none)), "None is empty, arrayFuncTypedef should return true")
- self.assertFalse(arrayFuncTypedef(np.array(full)), "Full is NOT empty, arrayFuncTypedef should return false")
+ self.assertTrue(arrayFuncTypedef(np.array(none)),
+ "None is empty, arrayFuncTypedef should return true")
+ self.assertFalse(arrayFuncTypedef(np.array(full)),
+ "Full is NOT empty, arrayFuncTypedef should return false")
def test_arrayFuncReturn(self):
none = arrayFuncReturn(0)
full = arrayFuncReturn(self.the_size)
self.assertTrue((len(none) == 0), "none should be empty")
- self.assertTrue((len(full) == self.the_size), "full should have " + str(self.the_size) + " elements")
+ self.assertTrue((len(full) == self.the_size),
+ f"full should have {self.the_size} elements")
def test_arrayFuncReturnTypedef(self):
none = arrayFuncReturnTypedef(0)
full = arrayFuncReturnTypedef(self.the_size)
self.assertTrue((len(none) == 0), "none should be empty")
- self.assertTrue((len(full) == self.the_size), "full should have " + str(self.the_size) + " elements")
+ self.assertTrue((len(full) == self.the_size),
+ f"full should have {self.the_size} elements")
if __name__ == '__main__':
- if np != None:
+ if np is not None:
unittest.main()
if __name__ == '__main__':
unittest.main()
-
from sample import Collector, ObjectType
from other import OtherObjectType
+
class CollectorOtherObjectType(unittest.TestCase):
'''Test cases for Collector << OtherObjectType'''
collector << obj
self.assertEqual(collector.items()[0], obj.identifier() * 2)
+
if __name__ == '__main__':
unittest.main()
-
from sample import NoImplicitConversion
from other import ExtendsNoImplicitConversion
+
class ConversionOperatorForClassWithoutImplicitConversionsTest(unittest.TestCase):
- '''Tests calling NoImplicitConversion constructor using a ExtendsNoImplicitConversion parameter.'''
+ '''Tests calling NoImplicitConversion constructor using a
+ ExtendsNoImplicitConversion parameter.'''
def testNoImplicitConversion(self):
'''Basic test to see if the NoImplicitConversion is Ok.'''
# NoImplicitConversion.receivesNoImplicitConversionByValue(NoImplicitConversion)
self.assertEqual(obj.objId(), NoImplicitConversion.receivesNoImplicitConversionByValue(obj))
# NoImplicitConversion.receivesNoImplicitConversionByPointer(NoImplicitConversion*)
- self.assertEqual(obj.objId(), NoImplicitConversion.receivesNoImplicitConversionByPointer(obj))
+ self.assertEqual(obj.objId(),
+ NoImplicitConversion.receivesNoImplicitConversionByPointer(obj))
# NoImplicitConversion.receivesNoImplicitConversionByReference(NoImplicitConversion&)
- self.assertEqual(obj.objId(), NoImplicitConversion.receivesNoImplicitConversionByReference(obj))
+ self.assertEqual(obj.objId(),
+ NoImplicitConversion.receivesNoImplicitConversionByReference(obj))
def testPassingExtendsNoImplicitConversionAsNoImplicitConversionByValue(self):
- '''Gives an ExtendsNoImplicitConversion object to a function expecting a NoImplicitConversion, passing by value.'''
+ '''Gives an ExtendsNoImplicitConversion object to a function expecting a
+ NoImplicitConversion, passing by value.'''
obj = ExtendsNoImplicitConversion(123)
self.assertEqual(obj.objId(), NoImplicitConversion.receivesNoImplicitConversionByValue(obj))
def testPassingExtendsNoImplicitConversionAsNoImplicitConversionByReference(self):
- '''Gives an ExtendsNoImplicitConversion object to a function expecting a NoImplicitConversion, passing by reference.'''
+ '''Gives an ExtendsNoImplicitConversion object to a function expecting a
+ NoImplicitConversion, passing by reference.'''
obj = ExtendsNoImplicitConversion(123)
- self.assertEqual(obj.objId(), NoImplicitConversion.receivesNoImplicitConversionByReference(obj))
+ self.assertEqual(obj.objId(),
+ NoImplicitConversion.receivesNoImplicitConversionByReference(obj))
def testPassingExtendsNoImplicitConversionAsNoImplicitConversionByPointer(self):
- '''Gives an ExtendsNoImplicitConversion object to a function expecting a NoImplicitConversion, passing by pointer.
- This should not be accepted, since pointers should not be converted.'''
+ '''Gives an ExtendsNoImplicitConversion object to a function expecting
+ a NoImplicitConversion, passing by pointer. This should not be
+ accepted, since pointers should not be converted.'''
obj = ExtendsNoImplicitConversion(123)
- self.assertRaises(TypeError, NoImplicitConversion.receivesNoImplicitConversionByPointer, obj)
+ self.assertRaises(TypeError,
+ NoImplicitConversion.receivesNoImplicitConversionByPointer, obj)
if __name__ == '__main__':
unittest.main()
-
from sample import Point
from other import Number
+
class PointOperationsWithNumber(unittest.TestCase):
'''Test cases for libsample's Point multiply operator defined in libother module.'''
num = Number(11)
self.assertEqual(pt * num.value(), pt * 11)
+
if __name__ == '__main__':
unittest.main()
-
shutil.copyfile(src, dst)
sys.path.append(os.fspath(workdir))
+
class TestModuleReloading(unittest.TestCase):
def testModuleReloading(self):
reload(test_module)
self.assertFalse(oldObject is test_module.obj)
+
if __name__ == "__main__":
unittest.main()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-'''Tests calling Str constructor using a Number parameter, being that number defines a cast operator to Str.'''
+'''Tests calling Str constructor using a Number parameter, being that number defines
+ a cast operator to Str.'''
import os
import sys
from sample import Str
from other import Number
+
class NewCtorOperatorTest(unittest.TestCase):
- '''Tests calling Str constructor using a Number parameter, being that number defines a cast operator to Str.'''
+ '''Tests calling Str constructor using a Number parameter, being that number
+ defines a cast operator to Str.'''
def testNumber(self):
'''Basic test to see if the Number class is Ok.'''
'''Try to build a Str from 'sample' module with a Number argument from 'other' module.'''
value = 123
num = Number(value)
- string = Str(num)
+ string = Str(num) # noqa: F841
+
if __name__ == '__main__':
unittest.main()
-
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
from shiboken_paths import init_paths
init_paths()
-from sample import *
-from other import *
+from sample import HandleHolder
from shiboken6 import Shiboken
+
class TestHashFuncs (unittest.TestCase):
def testIt(self):
self.assertEqual(hash1_2, hash1)
-
if __name__ == '__main__':
unittest.main()
from sample import Abstract, Derived
from other import OtherDerived, Number
+
class Multiple(Derived, Number):
def __init__(self):
Derived.__init__(self, 42)
def testCall(self):
return True
+
class OtherDeviant(OtherDerived):
def __init__(self):
OtherDerived.__init__(self)
def className(self):
return 'OtherDeviant'
+
class MultipleTest(unittest.TestCase):
'''Test case for Multiple derived class'''
self.assertTrue(o.value(), 42)
self.assertTrue(o.testCall())
+
class OtherDerivedTest(unittest.TestCase):
'''Test case for OtherDerived class'''
self.assertTrue(inherited_methods.issubset(dir(OtherDerived)))
def testReimplementedPureVirtualMethodCall(self):
- '''Test if a Python override of a implemented pure virtual method is correctly called from C++.'''
+ '''Test if a Python override of a implemented pure virtual method is
+ correctly called from C++.'''
d = OtherDeviant()
d.callPureVirtual()
self.assertTrue(d.pure_virtual_called)
def testReimplementedVirtualMethodCall(self):
- '''Test if a Python override of a reimplemented virtual method is correctly called from C++.'''
+ '''Test if a Python override of a reimplemented virtual method is
+ correctly called from C++.'''
d = OtherDeviant()
d.callUnpureVirtual()
self.assertTrue(d.unpure_virtual_called)
self.assertEqual(d.getClassName(), 'OtherDerived')
def testReimplementedVirtualMethodCallReturningString(self):
- '''Test if a Python override of a reimplemented virtual method is correctly called from C++.'''
+ '''Test if a Python override of a reimplemented virtual method is
+ correctly called from C++.'''
d = OtherDeviant()
self.assertEqual(d.className(), 'OtherDeviant')
self.assertEqual(d.getClassName(), 'OtherDeviant')
d = OtherDerived(objId)
self.assertEqual(Abstract.getObjectId(d), objId)
+
if __name__ == '__main__':
unittest.main()
-
from shiboken_paths import init_paths
init_paths()
-from other import (OtherValueWithUnitUser, ValueWithUnitIntInch,
- ValueWithUnitIntMillimeter)
+from other import (OtherValueWithUnitUser, ValueWithUnitIntMillimeter)
from sample import (ValueWithUnitDoubleMillimeter)
from other import OtherObjectType
from shiboken_test_helper import objectFullname
-from shiboken6 import Shiboken
-
from shibokensupport.signature import get_signature
def testNamespaceFromOtherModule(self):
argType = get_signature(OtherObjectType.enumAsInt).parameters["value"].annotation
self.assertEqual(objectFullname(argType),
- "sample.SampleNamespace.SomeClass.PublicScopedEnum")
+ "sample.SampleNamespace.SomeClass.PublicScopedEnum")
if __name__ == '__main__':
from shiboken_paths import init_paths
init_paths()
-from smart import Integer
-from sample import Str
from other import SmartPtrTester
--- /dev/null
+#!/usr/bin/env python
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+"""PYSIDE-2404: Test whether star imports work as they require special handling
+ by the lazy initialization."""
+
+import os
+import sys
+import unittest
+
+from pathlib import Path
+sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
+from shiboken_paths import init_paths
+init_paths()
+
+SHIBOKEN_NAME = "shiboken6.Shiboken"
+MINIMAL_NAME = "minimal"
+OTHER_NAME = "other"
+
+shiboken_loaded = 1 if sys.modules.get(SHIBOKEN_NAME) else 0
+minimal_loaded = 1 if sys.modules.get(MINIMAL_NAME) else 0
+other_loaded = 1 if sys.modules.get(OTHER_NAME) else 0
+
+from minimal import * # noqa: F403
+
+shiboken_loaded += 2 if sys.modules.get(SHIBOKEN_NAME) else 0
+minimal_loaded += 2 if sys.modules.get(MINIMAL_NAME) else 0
+other_loaded += 2 if sys.modules.get(OTHER_NAME) else 0
+
+from other import Number # noqa: F403
+from other import * # noqa: F403
+
+shiboken_loaded += 4 if sys.modules.get(SHIBOKEN_NAME) else 0
+minimal_loaded += 4 if sys.modules.get(MINIMAL_NAME) else 0
+other_loaded = +4 if sys.modules.get(OTHER_NAME) else 0
+
+import shiboken6.Shiboken # noqa: F401 F403
+
+shiboken_loaded += 8 if sys.modules.get(SHIBOKEN_NAME) else 0
+
+
+class ValTest(unittest.TestCase):
+
+ def test(self):
+ val_id = 123
+ val = Val(val_id) # noqa: F405
+ self.assertEqual(val.valId(), val_id)
+
+
+class Simple(Number):
+
+ def __init__(self):
+ Number.__init__(self, 42)
+
+
+class OtherTest(unittest.TestCase):
+
+ def testConstructor(self):
+ o = Simple()
+ self.assertTrue(isinstance(o, Number))
+
+
+class StarImportTest(unittest.TestCase):
+ """
+ This test is meant for Lazy Init.
+ We explicitly choose modules which are able to lazy load.
+
+ The ValTest:
+ ------------
+ We load something with `import *`.
+ There is no module from our known ones imported.
+ This means we need stack introspection to find out that this was
+ a star import and we must disable lazyness.
+
+ The OtherTest:
+ --------------
+ We load something normally that should be lazy.
+ After that, we follow with a star import.
+ Now the stack introspection does not work, because the loading is
+ cached. The first import did a lazy load. The following star import
+ needs to undo the lazyness. But now we have a redirected import.
+
+ All tests simply check if the objects are real and not just names.
+ The <module>_loaded tests prevend upcoming internal dependencies.
+
+ To make sure that Shiboken is really not involved, it is checked
+ and really imported afterwards (ensuring nothing is misspelled).
+ """
+
+ def testStar(self):
+ self.assertEqual(other_loaded, 4)
+ self.assertEqual(minimal_loaded, 6)
+ self.assertEqual(shiboken_loaded, 14)
+ # Interesting effect: Did not expect that shiboken is loaded at all.
+
+
+if __name__ == '__main__':
+ unittest.main()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-from other import *
-from sample import *
+import os
+import sys
+
+from pathlib import Path
+sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
+from shiboken_paths import init_paths
+init_paths()
+
+from other import OtherObjectType
+from sample import ObjectType
class MyObjectType(ObjectType):
pass
+
class MyOtherObjectType(OtherObjectType):
value = 10
from shiboken_paths import init_paths
init_paths()
-from sample import Abstract, Base1, Derived, MDerived1, MDerived3, SonOfMDerived1
+from sample import Abstract, Base1, Derived
from other import OtherMultipleDerived
+
class TypeDiscoveryTest(unittest.TestCase):
def testPureVirtualsOfImpossibleTypeDiscovery(self):
self.assertEqual(type(a), Derived)
def testMultipleInheritance(self):
- obj = OtherMultipleDerived.createObject("Base1");
+ obj = OtherMultipleDerived.createObject("Base1")
self.assertEqual(type(obj), Base1)
# PYSIDE-868: In case of multiple inheritance, a factory
# function will return the base class wrapper.
- obj = OtherMultipleDerived.createObject("MDerived1");
+ obj = OtherMultipleDerived.createObject("MDerived1")
self.assertEqual(type(obj), Base1)
- obj = OtherMultipleDerived.createObject("SonOfMDerived1");
+ obj = OtherMultipleDerived.createObject("SonOfMDerived1")
self.assertEqual(type(obj), Base1)
- obj = OtherMultipleDerived.createObject("MDerived3");
+ obj = OtherMultipleDerived.createObject("MDerived3")
self.assertEqual(type(obj), Base1)
- obj = OtherMultipleDerived.createObject("OtherMultipleDerived");
+ obj = OtherMultipleDerived.createObject("OtherMultipleDerived")
self.assertEqual(type(obj), Base1)
+
if __name__ == '__main__':
unittest.main()
init_paths()
from other import Number
+
class UserDefinedPrimitiveTypeFromRequiredModuleTest(unittest.TestCase):
def testUsersPrimitiveFromRequiredModuleAsArgument(self):
cpx = number.toComplex()
self.assertEqual(number.value(), int(cpx.real))
+
if __name__ == '__main__':
unittest.main()
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
from shiboken_paths import init_paths
init_paths()
-from sample import *
-from other import *
+from sample import Abstract, ObjectType
+from other import OtherDerived
+
class Foo(OtherDerived):
def __init__(self):
- Abstract.__init__(self, 2) # this should raise an exception
+ Abstract.__init__(self, 2) # this should raise an exception
+
class Foo2(ObjectType, OtherDerived):
def __init__(self):
ObjectType.__init__(self)
- Abstract.__init__(self, 2) # this should raise an exception
+ Abstract.__init__(self, 2) # this should raise an exception
class WrongCtorTest(unittest.TestCase):
void QtXmlToSphinxTest::testSnippetExtraction_data()
{
QTest::addColumn<QByteArray>("file");
- QTest::addColumn<QString>("id");
+ QTest::addColumn<QLatin1StringView>("id");
QTest::addColumn<QString>("expected");
const char *fileCpp = R"(bla
// ![snip2] // ![snip3]
)";
- const QString id = u"snip2"_s;
+ constexpr auto id = "snip2"_L1;
const QString expected = uR"(snip2_line1
snip2_line2
)"_s;
void QtXmlToSphinxTest::testSnippetExtraction()
{
QFETCH(QByteArray, file);
- QFETCH(QString, id);
+ QFETCH(QLatin1StringView, id);
QFETCH(QString, expected);
QBuffer buffer(&file);
delCalled = False
+
class MyObject(sample.ObjectType):
def __del__(self):
global delCalled
delCalled = True
+
class TestDel(unittest.TestCase):
def testIt(self):
a = MyObject()
gc.collect()
self.assertTrue(delCalled)
+
if __name__ == '__main__':
unittest.main()
-
from sample import Abstract
+
class Incomplete(Abstract):
def __init__(self):
Abstract.__init__(self)
+
class Concrete(Abstract):
def __init__(self):
Abstract.__init__(self)
# Python and calling it from C++ is undefined until it's decided how to
# cast the Python data types to void pointers
c = Concrete()
- self.assertEqual(c.pureVirtualReturningVoidPtr(),42)
-
- def testReimplementedVirtualMethodCall(self):
- '''Test if instanciation of an abstract class raises the correct exception.'''
- i = Concrete()
- self.assertRaises(NotImplementedError, i.callPureVirtual)
+ self.assertEqual(c.pureVirtualReturningVoidPtr(), 42)
def testReimplementedVirtualMethodCall(self):
'''Test if a Python override of a virtual method is correctly called from C++.'''
c.callVirtualGettingEnum(Abstract.Short)
self.assertTrue(c.virtual_getting_enum)
+
if __name__ == '__main__':
unittest.main()
-
init_paths()
from sample import SampleNamespace, ObjectType, Point
+
class TestAddedFunctionsWithSimilarTypes(unittest.TestCase):
'''Adds new signatures very similar to already existing ones.'''
control = len(obj.objectName())
self.assertEqual(SampleNamespace.passReferenceToObjectType(obj), control)
+
if __name__ == '__main__':
unittest.main()
init_paths()
from sample import sum2d, sumproduct
+
class TestAddedFunctionsWithContainerArgs(unittest.TestCase):
'''Tests added functions with nested and multi-argument container types.'''
def testNestedContainerType(self):
'''Test added function with single-argument containers.'''
- values = [[1,2],[3,4,5],[6]]
+ values = [[1, 2], [3, 4, 5], [6]]
self.assertEqual(sum2d(values), 21)
def testMultiArgContainerType(self):
'''Test added function with a two-argument container.'''
- values = [(1,2),(3,4),(5,6)]
+ values = [(1, 2), (3, 4), (5, 6)]
self.assertEqual(sumproduct(values), 44)
+
if __name__ == '__main__':
unittest.main()
from sample import Modifications, Point
+
class ArgumentModificationsTest(unittest.TestCase):
'''Test cases for method arguments modifications performed as described on typesystem.'''
def testArgRemoval1(self):
'''Tests argument removal modifications on Modifications.argRemoval1.'''
- # void [-> PyObject*] argRemoval1(int, bool, Point = Point(1, 2) [removed], Point = Point(3, 4) [removed], int = 333)
+ # void [-> PyObject*] argRemoval1(int, bool, Point = Point(1, 2) [removed],
+ # Point = Point(3, 4) [removed], int = 333)
# code-injection: returns tuple with received parameters plus removed ones
a0, a1, a2 = 1, True, 2
self.assertEqual(self.mods.argRemoval1(a0, a1), (a0, a1, Point(1, 2), Point(3, 4), 333))
def testArgRemoval2(self):
'''Tests argument removal modifications on Modifications.argRemoval2.'''
- # void [-> PyObject*] argRemoval2(int, bool, Point = Point(1, 2) [removed], Point = Point(3, 4) [removed], int = 333)
+ # void [-> PyObject*] argRemoval2(int, bool, Point = Point(1, 2)
+ # [removed], Point = Point(3, 4) [removed], int = 333)
# code-injection: returns tuple with received parameters plus removed ones
a0, a1, a2 = 1, True, 2
self.assertEqual(self.mods.argRemoval2(a0, a1), (a0, a1, Point(1, 2), Point(3, 4), 333))
def testArgRemoval3(self):
'''Tests argument removal modifications on Modifications.argRemoval3.'''
- # void [-> PyObject*] argRemoval3(int, Point = Point(1, 2) [removed], bool = true, Point = Point(3, 4) [removed], int = 333)
+ # void [-> PyObject*] argRemoval3(int, Point = Point(1, 2) [removed],
+ # bool = true, Point = Point(3, 4) [removed], int = 333)
# code-injection: returns tuple with received parameters plus removed ones
a0, a1, a2 = 1, True, 2
self.assertEqual(self.mods.argRemoval3(a0), (a0, Point(1, 2), True, Point(3, 4), 333))
def testArgRemoval4(self):
'''Tests argument removal modifications on Modifications.argRemoval4.'''
- # void [-> PyObject*] argRemoval4(int, Point [removed, new val = Point(6, 9)], bool, Point = Point(3, 4) [removed], int = 333)
+ # void [-> PyObject*] argRemoval4(int, Point [removed, new val = Point(6, 9)], bool,
+ # Point = Point(3, 4) [removed], int = 333)
# code-injection: returns tuple with received parameters plus removed ones
a0, a1, a2 = 1, True, 2
self.assertRaises(TypeError, self.mods.argRemoval4, a0)
# code-injection: returns tuple with received parameters plus removed ones
self.assertEqual(self.mods.argRemoval5(a0, a1, a2), (200, a0, a1, a2))
+
if __name__ == '__main__':
unittest.main()
-
except ImportError:
pass
+
class ArrayTester(unittest.TestCase):
'''Test case for NumPy arrays.'''
def testIntArray(self):
- intList = numpy.array([1, 2, 3, 4], dtype = 'int32')
+ intList = numpy.array([1, 2, 3, 4], dtype='int32')
self.assertEqual(sample.sumIntArray(intList), 10)
def testDoubleArray(self):
- doubleList = numpy.array([1, 2, 3, 4], dtype = 'double')
+ doubleList = numpy.array([1, 2, 3, 4], dtype='double')
self.assertEqual(sample.sumDoubleArray(doubleList), 10)
def testIntMatrix(self):
- intMatrix = numpy.array([[1, 2, 3], [4, 5, 6]], dtype = 'int32')
+ intMatrix = numpy.array([[1, 2, 3], [4, 5, 6]], dtype='int32')
self.assertEqual(sample.sumIntMatrix(intMatrix), 21)
def testDoubleMatrix(self):
- doubleMatrix = numpy.array([[1, 2, 3], [4, 5, 6]], dtype = 'double')
+ doubleMatrix = numpy.array([[1, 2, 3], [4, 5, 6]], dtype='double')
self.assertEqual(sample.sumDoubleMatrix(doubleMatrix), 21)
+
if __name__ == '__main__' and hasNumPy:
unittest.main()
init_paths()
import sample
+
class ArrayTester(unittest.TestCase):
'''Test case for arrays.'''
doubleList = [1.2, 2.3, 3.4, 4.5]
self.assertEqual(sample.sumDoubleArray(doubleList), 11.4)
+
if __name__ == '__main__':
unittest.main()
from shiboken_paths import init_paths
init_paths()
-from sample import *
+from sample import ObjectType
+
class Bug554:
def crash(self):
class Crasher(ObjectType):
pass
+
if __name__ == '__main__':
bug = Bug554()
bug.crash()
-
-
class ObjectTypeTest(unittest.TestCase):
- '''Test cases to avoid declaring Shiboken classes with multiple inheritance from old style classes.'''
+ '''Test cases to avoid declaring Shiboken classes with multiple inheritance
+ from old style classes.'''
def testObjectTypeNewStype(self):
defineNewStyle()
-
if __name__ == '__main__':
unittest.main()
-
def testConcatPythonStringAndByteArray(self):
# Test concatenation of a Python string with a ByteArray, in this order.
- concat_python_string_add_qbytearray_worked = True
+ concat_python_string_add_qbytearray_worked = True # noqa: F841
ba = ByteArray('foo')
result = 'bar\x00' + ba
self.assertEqual(type(result), ByteArray)
# ByteArray[x] where x is a valid index (reverse order).
string = 'abcdefgh'
obj = ByteArray(string)
- for i in range(len(string)-1, 0, -1):
+ for i in range(len(string) - 1, 0, -1):
self.assertEqual(obj[i], bytes(string[i], "UTF8"))
def testOutOfRange(self):
# ByteArray[x] where x is out of index.
string = '1234567'
obj = ByteArray(string)
- self.assertRaises(IndexError, lambda :obj[len(string)])
+ self.assertRaises(IndexError, lambda: obj[len(string)])
def testNullStrings(self):
ba = ByteArray('\x00')
from shiboken_paths import init_paths
init_paths()
-from sample import *
+from sample import ObjectType
+
class ReturnOfChildTest(unittest.TestCase):
'''The BlackBox class has cases of ownership transference between C++ and Python.'''
gc.collect()
self.assertRaises(RuntimeError, child.objectName)
+
if __name__ == '__main__':
unittest.main()
-
from sample import Derived, Point, ObjectType
+
class TestAccessingCppFields(unittest.TestCase):
'''Simple test case for accessing the exposed C++ class fields.'''
self.assertEqual(d.primitiveField, int(value))
# attribution with invalid type
- self.assertRaises(TypeError, lambda : setattr(d, 'primitiveField', None))
+ self.assertRaises(TypeError, lambda: setattr(d, 'primitiveField', None))
def testAccessingRenamedFields(self):
'''Reads and writes a renamed field.'''
self.assertNotEqual(d.userPrimitiveField, old_value)
# attribution with invalid type
- self.assertRaises(TypeError, lambda : setattr(d, 'userPrimitiveField', None))
+ self.assertRaises(TypeError, lambda: setattr(d, 'userPrimitiveField', None))
def testAccessingValueTypeField(self):
'''Reads and writes a value type (in this case a 'Point') field.'''
self.assertEqual(type(d.valueTypeField), Point)
# attribution
- old_value = d.valueTypeField
+ old_value = d.valueTypeField # noqa: F841
new_value = Point(-10, 537)
d.valueTypeField = new_value
self.assertEqual(d.valueTypeField, new_value)
self.assertEqual(d.valueTypeField.y(), 20)
# attribution with invalid type
- self.assertRaises(TypeError, lambda : setattr(d, 'valueTypeField', 123))
+ self.assertRaises(TypeError, lambda: setattr(d, 'valueTypeField', 123))
def testAccessingObjectTypeField(self):
'''Reads and writes a object type (in this case an 'ObjectType') field.'''
self.assertEqual(d.objectTypeField, value)
# attribution with invalid type
- self.assertRaises(TypeError, lambda : setattr(d, 'objectTypeField', 123))
+ self.assertRaises(TypeError, lambda: setattr(d, 'objectTypeField', 123))
@unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount")
def testRefCountingAccessingObjectTypeField(self):
# attribution
old_value = d.bitField
new_value = 1
- d.bitField= new_value
+ d.bitField = new_value
self.assertEqual(d.bitField, new_value)
self.assertNotEqual(d.bitField, old_value)
self.assertEqual(d.bitField, int(value))
# attribution with invalid type
- self.assertRaises(TypeError, lambda : setattr(d, 'bitField', None))
+ self.assertRaises(TypeError, lambda: setattr(d, 'bitField', None))
if __name__ == '__main__':
self.assertEqual(collector.size(), 5)
self.assertEqual(collector.items(), [2, 3, 5, 7, 11])
+
class CollectorExternalOperator(unittest.TestCase):
'''Test cases for external operators of Collector'''
if __name__ == '__main__':
unittest.main()
-
import sample
from sample import Point
+
class ComplexTest(unittest.TestCase):
'''Test case for conversions between C++ Complex class to Python complex class'''
def testUsingTuples(self):
cpx1, cpx2 = (1.2, 3.4), (5.6, 7.8)
- self.assertEqual(sample.sumComplexPair((cpx1, cpx2)), sample.sumComplexPair((complex(*cpx1), complex(*cpx2))))
+ self.assertEqual(sample.sumComplexPair((cpx1, cpx2)),
+ sample.sumComplexPair((complex(*cpx1), complex(*cpx2))))
cpx1, cpx2 = (1, 3), (5, 7)
- self.assertEqual(sample.sumComplexPair((cpx1, cpx2)), sample.sumComplexPair((complex(*cpx1), complex(*cpx2))))
+ self.assertEqual(sample.sumComplexPair((cpx1, cpx2)),
+ sample.sumComplexPair((complex(*cpx1), complex(*cpx2))))
cpx1, cpx2 = (1.2, 3), (5.6, 7)
- self.assertEqual(sample.sumComplexPair((cpx1, cpx2)), sample.sumComplexPair((complex(*cpx1), complex(*cpx2))))
+ self.assertEqual(sample.sumComplexPair((cpx1, cpx2)),
+ sample.sumComplexPair((complex(*cpx1), complex(*cpx2))))
cpx1, cpx2 = (1, 2, 3), (4, 5, 7)
self.assertRaises(TypeError, sample.sumComplexPair, (cpx1, cpx2))
cpx1, cpx2 = ('1', '2'), ('4', '5')
if __name__ == '__main__':
unittest.main()
-
from sample import Time, StrList
+
class ConversionOperatorTest(unittest.TestCase):
'''Test cases for implicit conversion generated by conversion operator.'''
def testConversionOperator(self):
- '''Time defined an conversion operator for Str, so passing a Time object to a method expecting a Str should work.'''
+ '''Time defined an conversion operator for Str, so passing a Time object
+ to a method expecting a Str should work.'''
t = Time(1, 2, 3)
t_str = t.toString()
sl = StrList()
self.assertEqual(len(sl), 1)
self.assertEqual(sl[0], t_str)
+
if __name__ == '__main__':
unittest.main()
-
if __name__ == '__main__':
unittest.main()
-
from sample import CtorConvRule
+
class TestCtorConvRule(unittest.TestCase):
'''Simple test case for CtorConvRule'''
obj = CtorConvRule(value)
self.assertEqual(obj.value(), value + 1)
+
if __name__ == '__main__':
unittest.main()
-
from sample import ObjectModel
-
class ObjTest(unittest.TestCase):
def test_cyclic_dependency_withParent(self):
# turn off automatic garbage collection, to be able to trigger it
# at the 'right' time
gc.disable()
- alive = lambda :sum(isinstance(o, CyclicObject) for o in gc.get_objects() )
+ alive = lambda: sum(isinstance(o, CyclicObject) for o in gc.get_objects()) # noqa: E731
#
# first proof that the wizard is only destructed by the garbage
# turn off automatic garbage collection, to be able to trigger it
# at the 'right' time
gc.disable()
- alive = lambda :sum(isinstance(o, CyclicObject) for o in gc.get_objects() )
+ alive = lambda: sum(isinstance(o, CyclicObject) for o in gc.get_objects()) # noqa: E731
#
# first proof that the wizard is only destructed by the garbage
gc.collect()
self.assertFalse(alive())
+
if __name__ == '__main__':
unittest.main()
-
from sample import SbkDate
+
class DateConversionTest(unittest.TestCase):
def testConstructorWithDateObject(self):
self.assertTrue(cDate.month(), pyDate.month)
self.assertTrue(cDate.year(), pyDate.year)
+
if __name__ == '__main__':
unittest.main()
-
from sample import SampleNamespace, Point, ObjectType, ObjectModel
+
class DecisorTest(unittest.TestCase):
'''Test cases for the method overload decisor.'''
'''Call methods overloads that receive parent and inheritor classes' instances.'''
objecttype = ObjectType()
objectmodel = ObjectModel()
- self.assertEqual(ObjectModel.receivesObjectTypeFamily(objecttype), ObjectModel.ObjectTypeCalled)
- self.assertNotEqual(ObjectModel.receivesObjectTypeFamily(objecttype), ObjectModel.ObjectModelCalled)
- self.assertEqual(ObjectModel.receivesObjectTypeFamily(objectmodel), ObjectModel.ObjectModelCalled)
- self.assertNotEqual(ObjectModel.receivesObjectTypeFamily(objectmodel), ObjectModel.ObjectTypeCalled)
+ self.assertEqual(ObjectModel.receivesObjectTypeFamily(objecttype),
+ ObjectModel.ObjectTypeCalled)
+ self.assertNotEqual(ObjectModel.receivesObjectTypeFamily(objecttype),
+ ObjectModel.ObjectModelCalled)
+ self.assertEqual(ObjectModel.receivesObjectTypeFamily(objectmodel),
+ ObjectModel.ObjectModelCalled)
+ self.assertNotEqual(ObjectModel.receivesObjectTypeFamily(objectmodel),
+ ObjectModel.ObjectTypeCalled)
+
if __name__ == '__main__':
unittest.main()
-
import sample
from shiboken6 import Shiboken
+
class DeleteTest(unittest.TestCase):
def testNonCppWrapperClassDelete(self):
- """Would segfault when shiboken.delete called on obj not created from
- Python """
+ """Would segfault when shiboken.delete called on obj not created from Python."""
obj = sample.ObjectType()
child = obj.createChild(None)
Shiboken.delete(child)
assert not Shiboken.isValid(child)
+
if __name__ == '__main__':
unittest.main()
-
from sample import ObjectType
+
class TestDeprecatedCall(unittest.TestCase):
def testCallWithError(self):
o = ObjectType()
warnings.simplefilter('error')
self.assertRaises(DeprecationWarning, o.deprecatedFunction)
+
if __name__ == '__main__':
unittest.main()
import sample
from sample import Abstract, Derived, DerivedUsingCt, OverloadedFuncEnum
+
class Deviant(Derived):
def __init__(self):
Derived.__init__(self)
def className(self):
return 'Deviant'
+
+class ImplementVirtualWithOutParameter(Derived):
+ def __init__(self, value):
+ super().__init__()
+ self._value = value
+
+ def virtualWithOutParameter(self):
+ return self._value
+
+
class DerivedTest(unittest.TestCase):
'''Test case for Derived class'''
self.assertEqual(type(result), OverloadedFuncEnum)
def testOverloadedMethodCallWithWrongNumberOfArguments(self):
- '''Test if a call to an overloaded method with the wrong number of arguments raises an exception.'''
+ '''Test if a call to an overloaded method with the wrong number of arguments
+ raises an exception.'''
derived = Derived()
self.assertRaises(TypeError, derived.otherOverloaded, 1, 2, True)
def testReimplementedPureVirtualMethodCall(self):
- '''Test if a Python override of a implemented pure virtual method is correctly called from C++.'''
+ '''Test if a Python override of a implemented pure virtual method is
+ correctly called from C++.'''
d = Deviant()
d.callPureVirtual()
self.assertTrue(d.pure_virtual_called)
def testReimplementedVirtualMethodCall(self):
- '''Test if a Python override of a reimplemented virtual method is correctly called from C++.'''
+ '''Test if a Python override of a reimplemented virtual method is
+ correctly called from C++.'''
d = Deviant()
d.callUnpureVirtual()
self.assertTrue(d.unpure_virtual_called)
self.assertEqual(d.getClassName(), 'Derived')
def testReimplementedVirtualMethodCallReturningString(self):
- '''Test if a Python override of a reimplemented virtual method is correctly called from C++.'''
+ '''Test if a Python override of a reimplemented virtual method is
+ correctly called from C++.'''
d = Deviant()
self.assertEqual(d.className(), 'Deviant')
self.assertEqual(d.getClassName(), 'Deviant')
self.assertEqual(Abstract.getObjectId(d), objId)
def testObjectCreationWithParentType(self):
- '''Derived class creates an instance of itself in C++ and returns it as a pointer to its ancestor Abstract.'''
+ '''Derived class creates an instance of itself in C++ and returns it as
+ a pointer to its ancestor Abstract.'''
obj = Derived.createObject()
self.assertEqual(type(obj), Derived)
obj = DerivedUsingCt(42)
self.assertEqual(obj.value(), 42)
+ def testVirtualWithOutParameter(self):
+ d = Derived()
+ self.assertEqual(d.callVirtualWithOutParameter(), 42)
+
+ d = ImplementVirtualWithOutParameter(1)
+ self.assertEqual(d.callVirtualWithOutParameter(), 1)
+
if __name__ == '__main__':
unittest.main()
-
from sample import VirtualMethods, SimpleFile, Point
+
def MethodTypeCompat(func, instance):
return types.MethodType(func, instance)
def __init__(self):
VirtualMethods.__init__(self)
+
class Monkey(SimpleFile):
def __init__(self, filename):
SimpleFile.__init__(self, filename)
+
class DuckPunchingTest(unittest.TestCase):
'''Test case for duck punching (aka "monkey patching").'''
result2 = vm.virtualMethod0(pt, val, cpx, b)
self.assertEqual(result1, result2)
- self.assertEqual(result1, VirtualMethods.virtualMethod0(vm, pt, val, cpx, b) * self.multiplier)
+ self.assertEqual(result1,
+ VirtualMethods.virtualMethod0(vm, pt, val, cpx, b) * self.multiplier)
# This is done to decrease the refcount of the vm object
# allowing the object wrapper to be deleted before the
vm.virtualMethod0 = None
def testMonkeyPatchOnVirtualMethodWithInheritance(self):
- '''Injects new 'virtualMethod0' on an object that inherits from VirtualMethods and makes C++ call it.'''
+ '''Injects new 'virtualMethod0' on an object that inherits from
+ VirtualMethods and makes C++ call it.'''
duck = Duck()
pt, val, cpx, b = Point(1.1, 2.2), 4, complex(3.3, 4.4), True
result2 = duck.virtualMethod0(pt, val, cpx, b)
self.assertEqual(result1, result2)
- self.assertEqual(result1, VirtualMethods.virtualMethod0(duck, pt, val, cpx, b) * self.multiplier)
+ self.assertEqual(result1,
+ VirtualMethods.virtualMethod0(duck, pt, val, cpx, b) * self.multiplier)
duck.virtualMethod0 = None
if __name__ == '__main__':
unittest.main()
-
from sample import Echo
+
class TestEcho(unittest.TestCase):
'''Simple test case for Echo.echo'''
def testCallOperator(self):
e = Echo()
- self.assertEqual(e("Hello", 3), "Hello3");
+ self.assertEqual(e("Hello", 3), "Hello3")
+
+
if __name__ == '__main__':
unittest.main()
-
from shiboken_paths import init_paths
init_paths()
-import shiboken6
# This is needed after the introduction of BUILD_DIR.
import sample
from sample import SampleNamespace, ObjectType, Event
-from shibokensupport.signature import get_signature
def createTempFile():
def testEnumItemAsDefaultValueToIntArgument(self):
'''Calls function with an enum item as default value to an int argument.'''
- self.assertEqual(SampleNamespace.enumItemAsDefaultValueToIntArgument(), SampleNamespace.ZeroIn)
- self.assertEqual(SampleNamespace.enumItemAsDefaultValueToIntArgument(SampleNamespace.ZeroOut), SampleNamespace.ZeroOut)
+ self.assertEqual(SampleNamespace.enumItemAsDefaultValueToIntArgument(),
+ SampleNamespace.ZeroIn)
+ self.assertEqual(SampleNamespace.enumItemAsDefaultValueToIntArgument(SampleNamespace.ZeroOut), # noqa E:501
+ SampleNamespace.ZeroOut)
self.assertEqual(SampleNamespace.enumItemAsDefaultValueToIntArgument(123), 123)
def testAnonymousGlobalEnums(self):
def testEnumArgumentWithDefaultValue(self):
'''Option enumArgumentWithDefaultValue(Option opt = UnixTime);'''
self.assertEqual(SampleNamespace.enumArgumentWithDefaultValue(), SampleNamespace.UnixTime)
- self.assertEqual(SampleNamespace.enumArgumentWithDefaultValue(SampleNamespace.RandomNumber), SampleNamespace.RandomNumber)
+ self.assertEqual(SampleNamespace.enumArgumentWithDefaultValue(SampleNamespace.RandomNumber), # noqa E:501
+ SampleNamespace.RandomNumber)
class MyEvent(Event):
if __name__ == '__main__':
unittest.main()
-
import sample
from shiboken_test_helper import objectFullname
-from shiboken6 import Shiboken
-
from shibokensupport.signature import get_signature
sample.UnremovedNamespace.RemovedNamespace3_AnonymousEnum_Value0
def testNestedFunctionFromRemovedNamespace(self):
- self.assertEqual(sample.UnremovedNamespace.nestedMathSum(1, 2), 3)
+ self.assertEqual(sample.UnremovedNamespace.nestedMathSum(1, 2), 3)
if __name__ == '__main__':
unittest.main()
-
import os
import sys
-import time
import unittest
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
from shiboken_paths import init_paths
init_paths()
-from random import random
from sample import ObjectType, Event
objs = [ObjectType(), NoOverride(), Override()]
evaluated = ObjectType.processEvent(objs,
- Event(Event.BASIC_EVENT))
+ Event(Event.BASIC_EVENT))
self.assertEqual(evaluated, 3)
self.assertTrue(objs[2].called)
thread.start()
evaluated = ObjectType.processEvent(objs,
- Event(Event.BASIC_EVENT))
+ Event(Event.BASIC_EVENT))
thread.join()
from sample import ExceptionTest
+
class CppExceptionTest(unittest.TestCase):
def testVoid(self):
try:
et.voidThrowStdException(True)
- except:
+ except: # noqa: E722
exceptionCount += 1
et.voidThrowInt(False)
try:
et.voidThrowInt(True)
- except:
+ except: # noqa: E722
exceptionCount += 1
self.assertEqual(exceptionCount, 2)
exceptionCount = 0
et = ExceptionTest()
- result = et.intThrowStdException(False);
+ result = et.intThrowStdException(False)
try:
- result = et.intThrowStdException(True);
- except:
+ result = et.intThrowStdException(True)
+ except: # noqa: E722
exceptionCount += 1
- result = et.intThrowInt(False);
+ result = et.intThrowInt(False)
try:
- result = et.intThrowInt(True);
- except:
+ result = et.intThrowInt(True) # noqa: F841
+ except: # noqa: E722
exceptionCount += 1
self.assertEqual(exceptionCount, 2)
when return ownership modifications are generated."""
exceptionCount = 0
try:
- et = ExceptionTest.create(True);
- except:
+ et = ExceptionTest.create(True) # noqa: F841
+ except: # noqa: E722
exceptionCount += 1
self.assertEqual(exceptionCount, 1)
from sample import Data, Intersection, Union
+
class TestFilters(unittest.TestCase):
def testAnd(self):
self.assertEqual(type(inter), Intersection)
+
if __name__ == '__main__':
unittest.main()
from sample import HandleHolder
+
class HandleHolderTest(unittest.TestCase):
def testCreation(self):
holder = HandleHolder(HandleHolder.createHandle())
holder2 = HandleHolder(holder.handle2())
self.assertTrue(holder.compare2(holder2))
+
if __name__ == '__main__':
unittest.main()
from shiboken_paths import init_paths
init_paths()
-from sample import *
+from sample import ObjectType, Str
+
class HashableTest(unittest.TestCase):
h[o] = 2
self.assertTrue(h.get(o), 2)
+
if __name__ == '__main__':
unittest.main()
-
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
from shiboken_paths import init_paths
init_paths()
-from sample import *
+from sample import Reference
+
class TestLackOfDereferenceOperators (unittest.TestCase):
def testIf(self):
r = Reference()
self.assertFalse(hasattr(r, "__mul__"))
+
if __name__ == '__main__':
unittest.main()
cLongMin = -9223372036854775808
cLongMax = 9223372036854775807
+
class NumericTester(unittest.TestCase):
'''Helper class for numeric comparison testing'''
from sample import ImplicitConv, ObjectType
+
class ImplicitConvTest(unittest.TestCase):
'''Test case for implicit conversions'''
if __name__ == '__main__':
unittest.main()
-
from sample import SampleNamespace
+
class ScopeAndInheritanceTest(unittest.TestCase):
'''Test cases for finding scope in cases involving inheritance.'''
def testMethodCorrectlyWrapper(self):
'''A method returning a type declared in the scope of the method's
class parent must be found and the method correctly exported.'''
- meth = getattr(SampleNamespace.DerivedFromNamespace, 'methodReturningTypeFromParentScope')
+ meth = getattr(SampleNamespace.DerivedFromNamespace, # noqa: F841
+ 'methodReturningTypeFromParentScope')
+
if __name__ == '__main__':
unittest.main()
-
init_paths()
from sample import InjectCode
+
class MyInjectCode(InjectCode):
def __init__(self):
InjectCode.__init__(self)
def arrayMethod(self, values):
return self.multiplier * sum(values)
+
class InjectCodeTest(unittest.TestCase):
@unittest.skipIf(hasattr(sys, "pypy_version_info"),
- "PyPy type objects cannot be modified (yet) after creation")
+ "PyPy type objects cannot be modified (yet) after creation")
def testTypeNativeBeginning_TypeTargetBeginning(self):
ic = InjectCode()
self.assertEqual(str(ic), "Hi! I'm the inject code dummy class.")
self.assertEqual(result, sum(values))
def testCallReimplementedVirtualMethodWithArgumentRemovalAndArgumentTypeModification(self):
- '''Calls a reimplemented virtual method that had its first argument removed and the second modified.'''
+ '''Calls a reimplemented virtual method that had its first argument removed
+ and the second modified.'''
ic = MyInjectCode()
values = (1, 2, 3, 4, 5)
result = ic.callArrayMethod(values)
self.assertEqual(result, ic.multiplier * sum(values))
def testUsageOfTypeSystemCheckVariableOnPrimitiveType(self):
- '''When the sequence item is convertible to an integer -1 is returned, or -2 if its not convertible.'''
+ '''When the sequence item is convertible to an integer -1 is returned,
+ or -2 if its not convertible.'''
ic = InjectCode()
values = (1, 2, 3, 4, '5', 6.7)
result = ic.arrayMethod(values)
- fixedValues = [v for v in values if isinstance(v, int)]\
- + [-1 for v in values if isinstance(v, float)]\
- + [-2 for v in values if not isinstance(v, int) and not isinstance(v, float)]
- self.assertEqual(result, sum(fixedValues))
+ ints = [v for v in values if isinstance(v, int)]
+ floats = [-1 for v in values if isinstance(v, float)]
+ other = [-2 for v in values if not isinstance(v, int) and not isinstance(v, float)]
+ self.assertEqual(result, sum(ints + floats + other))
class IntArrayTest(unittest.TestCase):
ic = InjectCode()
self.assertEqual(sum([1, 2]) + len([1, 2]), ic.sumArrayAndLength(args))
+
if __name__ == '__main__':
unittest.main()
from sample import Derived
+
class TestInnerClass(unittest.TestCase):
def testInstaciate(self):
- d = Derived.SomeInnerClass()
+ d = Derived.SomeInnerClass() # noqa: F841
+
if __name__ == '__main__':
unittest.main()
-
from sample import IntList
+
class IntListTest(unittest.TestCase):
def testAutoFunctionsToBaseList(self):
self.assertEqual(il[1], int(432.1))
self.assertRaises(TypeError, il.__setitem__, 2, '78')
+
if __name__ == '__main__':
unittest.main()
class IntWrapperTest(unittest.TestCase):
def testOperators(self):
- ten1 = IntWrapper(10)
- ten2 = IntWrapper(10)
+ ten1 = IntWrapper(10)
+ ten2 = IntWrapper(10)
twenty = IntWrapper(20)
self.assertTrue(ten1 == ten2)
self.assertTrue(ten1 != twenty)
def testWrongTypeReturn(self):
model = ListModelWrong()
view = ObjectView(model)
- self.assertRaises(RuntimeWarning, view.getRawModelData) # calls model.data()
+ self.assertRaises(RuntimeWarning, view.getRawModelData) # calls model.data()
if __name__ == '__main__':
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-'''Test case for objects that keep references to other object without owning them (e.g. model/view relationships).'''
-
import os
import sys
import unittest
from sample import ObjectModel, ObjectView
+
class TestKeepReference(unittest.TestCase):
- '''Test case for objects that keep references to other object without owning them (e.g. model/view relationships).'''
+ '''Test case for objects that keep references to other object without
+ owning them (e.g. model/view relationships).'''
@unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount")
def testReferenceCounting(self):
self.assertEqual(sys.getrefcount(model), refcount1)
def testReferreedObjectSurvivalAfterContextEnd(self):
- '''Model-like object assigned to a view-like object must survive after get out of context.'''
+ '''Model-like object assigned to a view-like object must survive
+ after get out of context.'''
def createModelAndSetToView(view):
model = ObjectModel()
model.setObjectName('created model')
view.setModel(model)
view = ObjectView()
createModelAndSetToView(view)
- model = view.model()
+ model = view.model() # noqa: F841
+
if __name__ == '__main__':
unittest.main()
-
from sample import ListUser, Point, PointF
+
class ExtendedListUser(ListUser):
def __init__(self):
ListUser.__init__(self)
self.create_list_called = True
return [2, 3, 5, 7, 13]
+
class ListConversionTest(unittest.TestCase):
'''Test case for std::list container conversions'''
self.assertEqual(result, lst)
def testConversionInBothDirectionsWithSimilarContainer(self):
- '''Test converting a tuple, instead of the expected list, from Python to C++ and back again.'''
+ '''Test converting a tuple, instead of the expected list,
+ from Python to C++ and back again.'''
lu = ListUser()
lst = (3, 5, 7)
lu.setList(lst)
self.assertEqual(ListUser.ListOfPointF, ListUser.listOfPoints([PointF()]))
self.assertEqual(ListUser.ListOfPoint, ListUser.listOfPoints([Point()]))
+
if __name__ == '__main__':
unittest.main()
-
self.assertTrue(result)
def testReimplementedVirtualBlocker(self):
- '''Same as the basic case but blocker method is a C++ virtual reimplemented in Python and called from C++.'''
+ '''Same as the basic case but blocker method is a C++ virtual reimplemented
+ in Python and called from C++.'''
mybucket = MyBucket()
unlocker = Unlocker(mybucket)
unlocker.join()
self.assertTrue(result)
+
if __name__ == '__main__':
unittest.main()
from sample import MapUser
+
class ExtendedMapUser(MapUser):
def __init__(self):
MapUser.__init__(self)
def createMap(self):
self.create_map_called = True
- return {'two' : (complex(2.2, 2.2), 2),
- 'three' : (complex(3.3, 3.3), 3),
- 'five' : (complex(5.5, 5.5), 5),
- 'seven' : (complex(7.7, 7.7), 7)}
+ return {'two': (complex(2.2, 2.2), 2),
+ 'three': (complex(3.3, 3.3), 3),
+ 'five': (complex(5.5, 5.5), 5),
+ 'seven': (complex(7.7, 7.7), 7)}
+
class MapConversionTest(unittest.TestCase):
'''Test case for std::map container conversions'''
def testConversionInBothDirections(self):
'''Test converting a map from Python to C++ and back again.'''
mu = MapUser()
- map_ = {'odds' : [2, 4, 6], 'evens' : [3, 5, 7], 'primes' : [3, 4, 6]}
+ map_ = {'odds': [2, 4, 6], 'evens': [3, 5, 7], 'primes': [3, 4, 6]}
mu.setMap(map_)
result = mu.getMap()
self.assertEqual(result, map_)
def testConversionMapIntKeyValueTypeValue(self):
'''C++ signature: MapUser::passMapIntValueType(const std::map<int, const ByteArray>&)'''
mu = MapUser()
- map_ = {0 : 'string'}
+ map_ = {0: 'string'}
result = mu.passMapIntValueType(map_)
self.assertEqual(map_, result)
+
if __name__ == '__main__':
unittest.main()
from shiboken_paths import init_paths
init_paths()
-from sample import *
+from sample import Point
+
class MetaA(type):
pass
+
class A(object):
__metaclass__ = MetaA
+
MetaB = type(Point)
B = Point
+
class MetaC(MetaA, MetaB):
pass
+
+
class C(A, B):
__metaclass__ = MetaC
+
class D(C):
pass
+
class TestMetaClass(unittest.TestCase):
def testIt(self):
- w1 = C() # works
+ w1 = C() # works
w1.setX(1)
w1.setY(2)
- w2 = D() # should work!
+ w2 = D() # should work!
w2.setX(3)
w2.setY(4)
if __name__ == '__main__':
unittest.main()
-
-
from sample import ModelIndex, ReferentModelIndex, PersistentModelIndex
+
class TestCastOperator(unittest.TestCase):
def testCastOperatorReturningValue(self):
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-'''Test case for objects that keep references to other object without owning them (e.g. model/view relationships).'''
+'''Test case for objects that keep references to other object without owning them
+ (e.g. model/view relationships).'''
import os
import sys
object_name = 'test object'
+
class MyObject(ObjectType):
pass
+
class ListModelKeepsReference(ObjectModel):
def __init__(self, parent=None):
ObjectModel.__init__(self, parent)
def data(self):
return self.obj
+
class ListModelDoesntKeepsReference(ObjectModel):
def data(self):
obj = MyObject()
if __name__ == '__main__':
unittest.main()
-
from sample import Modifications, Point, ByteArray
+
class ExtModifications(Modifications):
def __init__(self):
Modifications.__init__(self)
gc.collect()
def testRenamedMethodAvailability(self):
- '''Test if Modification class really have renamed the 'className' virtual method to 'name'.'''
+ '''Test if Modification class really have renamed the 'className'
+ virtual method to 'name'.'''
self.assertTrue('className' not in dir(Modifications))
self.assertTrue('name' in dir(Modifications))
def testReimplementationOfRenamedVirtualMethod(self):
- '''Test if class inheriting from Modification class have the reimplementation of renamed virtual method called.'''
+ '''Test if class inheriting from Modification class have the reimplementation
+ of renamed virtual method called.'''
em = ExtModifications()
self.assertEqual(self.mods.name(), 'Modifications')
self.assertEqual(em.name(), 'ExtModifications')
self.assertEqual(self.mods.doublePlus(7), 14)
def testDefaultValueRemoval(self):
- '''Test if default value was removed from first argument of Modifications::increment(int).'''
+ '''Test if default value was removed from first argument of
+ Modifications::increment(int).'''
self.assertRaises(TypeError, self.mods.increment)
self.assertEqual(self.mods.increment(7), 8)
def testDefaultValueReplacement(self):
- '''Test if default values for both arguments of Modifications::power(int, int) were modified.'''
+ '''Test if default values for both arguments of Modifications::power(int, int)
+ were modified.'''
# original default values: int power(int base = 1, int exponent = 0);
self.assertNotEqual(self.mods.power(4), 1)
# modified default values: int power(int base = 2, int exponent = 1);
self.assertEqual(self.mods.power(5, 3), 5**3)
def testSetNewDefaultValue(self):
- '''Test if default value was correctly set to 10 for first argument of Modifications::timesTen(int).'''
+ '''Test if default value was correctly set to 10 for first argument of
+ Modifications::timesTen(int).'''
self.assertEqual(self.mods.timesTen(7), 70)
self.assertEqual(self.mods.timesTen(), 100)
def testArgumentRemovalAndReturnTypeModificationWithTypesystemTemplates1(self):
- '''Test modifications to method signature and return value using type system templates (case 1).'''
+ '''Test modifications to method signature and return value using type
+ system templates (case 1).'''
result, ok = self.mods.pointToPair(Point(2, 5))
self.assertEqual(type(ok), bool)
self.assertEqual(type(result), tuple)
self.assertEqual(result[1], 5.0)
def testArgumentRemovalAndReturnTypeModificationWithTypesystemTemplates2(self):
- '''Test modifications to method signature and return value using type system templates (case 2).'''
+ '''Test modifications to method signature and return value using
+ type system templates (case 2).'''
result, ok = self.mods.multiplyPointCoordsPlusValue(Point(2, 5), 4.1)
self.assertEqual(type(ok), bool)
self.assertEqual(type(result), float)
self.assertEqual(self.mods.overloaded(1, True, 2), Modifications.Overloaded_ibii)
# the others weren't modified
self.assertEqual(self.mods.overloaded(1, True, 2, False), Modifications.Overloaded_ibib)
- self.assertEqual(self.mods.overloaded(1, False, 2, Point(3, 4)), Modifications.Overloaded_ibiP)
+ self.assertEqual(self.mods.overloaded(1, False, 2, Point(3, 4)),
+ Modifications.Overloaded_ibiP)
self.assertRaises(TypeError, self.mods.overloaded, 1, True, Point(2, 3), Point(4, 5))
- self.assertEqual(self.mods.over(1, True, Point(2, 3), Point(4, 5)), Modifications.Overloaded_ibPP)
+ self.assertEqual(self.mods.over(1, True, Point(2, 3), Point(4, 5)),
+ Modifications.Overloaded_ibPP)
def testPointArrayModification(self):
points = (Point(1, 1), Point(2, 2))
'''Tests cases for ConstructorWithModifiedArgument class.'''
-import sys
import os
import sys
import unittest
from shiboken_paths import init_paths
init_paths()
-from sample import *
+from sample import ModifiedConstructor
class ConstructorWithModifiedArgumentTest(unittest.TestCase):
sampleClass = ModifiedConstructor("10")
self.assertTrue(sampleClass.retrieveValue(), 10)
+
if __name__ == '__main__':
unittest.main()
-
from sample import VirtualMethods, Str
+
class ExtendedVirtualMethods(VirtualMethods):
def __init__(self):
VirtualMethods.__init__(self)
self.callMe_called += 1
def getMargins(self):
- return tuple([m*2 for m in VirtualMethods.getMargins(self)])
+ return tuple([m * 2 for m in VirtualMethods.getMargins(self)])
class VirtualMethodsTest(unittest.TestCase):
removed_arg_value = 2011
default_value = 3000
result = self.evm.callSum4(a0, removed_arg_value, a1)
- self.assertEqual(result, (a0 - removed_arg_value + a1 + default_value) * self.evm.multiplier)
+ self.assertEqual(result,
+ (a0 - removed_arg_value + a1 + default_value) * self.evm.multiplier)
self.assertTrue(self.evm.sum4_called)
def testOverridenMethodResultModification(self):
def testExtendedAllArgumentsRemoved(self):
values = (10, 20, 30, 40)
self.evm.setMargins(*values)
- double = tuple([m*2 for m in values])
+ double = tuple([m * 2 for m in values])
self.assertEqual(self.evm.getMargins(), double)
def testExtendedAllArgumentsRemovedCallVirtual(self):
values = (10, 20, 30, 40)
self.evm.setMargins(*values)
- double = tuple([m*2 for m in values])
+ double = tuple([m * 2 for m in values])
self.assertEqual(self.evm.callGetMargins(), double)
+
if __name__ == '__main__':
unittest.main()
-
from shiboken_paths import init_paths
init_paths()
-from sample import *
+from sample import ObjectType, Point, Str
+
class SimpleUseCase(ObjectType, Str):
def __init__(self, name):
ObjectType.__init__(self)
Str.__init__(self, name)
+
class SimpleUseCaseReverse(Str, ObjectType):
def __init__(self, name):
ObjectType.__init__(self)
Str.__init__(self, name)
+
class SimpleUseCase2(SimpleUseCase):
def __init__(self, name):
SimpleUseCase.__init__(self, name)
+
class ComplexUseCase(SimpleUseCase2, Point):
def __init__(self, name):
SimpleUseCase2.__init__(self, name)
Point.__init__(self)
+
class ComplexUseCaseReverse(Point, SimpleUseCase2):
def __init__(self, name):
SimpleUseCase2.__init__(self, name)
Point.__init__(self)
+
class MultipleCppDerivedTest(unittest.TestCase):
- def testInstanciation(self):
+ def testInstantiation(self):
s = SimpleUseCase("Hi")
self.assertEqual(s, "Hi")
s.setObjectName(s)
self.assertEqual(s.objectName(), "Hi")
- def testInstanciation2(self):
+ def testInstantiation2(self):
s = SimpleUseCase2("Hi")
self.assertEqual(s, "Hi")
s.setObjectName(s)
self.assertEqual(s.objectName(), "Hi")
- def testComplexInstanciation(self):
+ def testComplexInstantiation(self):
c = ComplexUseCase("Hi")
self.assertEqual(c, "Hi")
c.setObjectName(c)
self.assertEqual(c.objectName(), "Hi")
- c.setX(2);
+ c.setX(2)
self.assertEqual(c.x(), 2)
+
class MultipleCppDerivedReverseTest(unittest.TestCase):
- def testInstanciation(self):
+ def testInstantiation(self):
s = SimpleUseCaseReverse("Hi")
self.assertEqual(s, "Hi")
s.setObjectName(s)
self.assertEqual(s.objectName(), "Hi")
- def testInstanciation2(self):
+ def testInstantiation2(self):
s = SimpleUseCase2("Hi")
self.assertEqual(s, "Hi")
s.setObjectName(s)
self.assertEqual(s.objectName(), "Hi")
- def testComplexInstanciation(self):
+ def testComplexInstantiation(self):
# PYSIDE-1564: This test can no longer work because of this MRO:
# ('ComplexUseCaseReverse', 'Point', 'SimpleUseCase2', 'SimpleUseCase',
# 'ObjectType', 'Str', 'Object', 'object')
# By multiple inheritance Point would be called first but has no argument.
with self.assertRaises(TypeError):
- c = ComplexUseCaseReverse("Hi")
+ c = ComplexUseCaseReverse("Hi") # noqa: F841
# c.setObjectName(c)
# self.assertEqual(c.objectName(), "Hi")
# c.setX(2);
# self.assertEqual(c, Point(2, 0))
+
if __name__ == '__main__':
unittest.main()
from shiboken_paths import init_paths
init_paths()
-from sample import Base1, Base2, Base3, Base4, Base5, Base6
+from sample import Base1, Base2
from sample import MDerived1, MDerived2, MDerived3, MDerived4, MDerived5, SonOfMDerived1
+
class ExtMDerived1(MDerived1):
def __init__(self):
MDerived1.__init__(self)
self.multiplier = 20
self.base2Method_called = False
+
def base2Method(self):
return Base2.base2Method(self) * self.multiplier
+
class MultipleDerivedTest(unittest.TestCase):
'''Test cases for multiple inheritance'''
self.assertTrue(issubclass(MDerived1, Base2))
def testCallToFunctionWithBase1ArgumentThatCastsBackToMDerived1(self):
- '''MDerived1 is passed as an Base1 argument to a method that returns it casted back to MDerived1.'''
+ '''MDerived1 is passed as an Base1 argument to a method that returns
+ it casted back to MDerived1.'''
a = MDerived1()
b = MDerived1.transformFromBase1(a)
self.assertEqual(a, b)
def testCallToFunctionWithBase2ArgumentThatCastsBackToMDerived1(self):
- '''MDerived1 is passed as an Base2 argument to a method that returns it casted back to MDerived1.'''
+ '''MDerived1 is passed as an Base2 argument to a method that returns
+ it casted back to MDerived1.'''
a = MDerived1()
b = MDerived1.transformFromBase2(a)
self.assertEqual(a, b)
def testPythonClassIsInstance(self):
- '''Python defined class ExtMDerived1 is instance of its parents MDerived1, Base1 and Base2.'''
+ '''Python defined class ExtMDerived1 is instance of its parents
+ MDerived1, Base1 and Base2.'''
a = ExtMDerived1()
self.assertTrue(isinstance(a, ExtMDerived1))
self.assertTrue(isinstance(a, MDerived1))
self.assertTrue(isinstance(a, Base2))
def testPythonClassIsSubclass(self):
- '''Python defined class ExtMDerived1 is subclass of its parents MDerived1, Base1 and Base2.'''
+ '''Python defined class ExtMDerived1 is subclass of its parents
+ MDerived1, Base1 and Base2.'''
self.assertTrue(issubclass(ExtMDerived1, MDerived1))
self.assertTrue(issubclass(ExtMDerived1, Base1))
self.assertTrue(issubclass(ExtMDerived1, Base2))
@unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount")
def testCastFromMDerived1ToBases(self):
- '''MDerived1 is casted by C++ to its parents and the binding must return the MDerived1 wrapper.'''
+ '''MDerived1 is casted by C++ to its parents and the binding must return the
+ MDerived1 wrapper.'''
a = MDerived1()
refcnt = sys.getrefcount(a)
b1 = a.castToBase1()
@unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount")
def testCastFromExtMDerived1ToMDerived1Bases(self):
- '''Python defined class ExtMDerived1 is casted by C++ to MDerived1 parents and the binding must return the correct ExtMDerived1 instance.'''
+ '''Python defined class ExtMDerived1 is casted by C++ to MDerived1 parents
+ and the binding must return the correct ExtMDerived1 instance.'''
a = ExtMDerived1()
refcnt = sys.getrefcount(a)
b1 = a.castToBase1()
@unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount")
def testCastFromSonOfMDerived1ToBases(self):
- '''SonOfMDerived1 is casted by C++ to its parents and the binding must return the SonOfMDerived1 wrapper.'''
+ '''SonOfMDerived1 is casted by C++ to its parents and the binding must return
+ the SonOfMDerived1 wrapper.'''
a = SonOfMDerived1()
refcnt = sys.getrefcount(a)
md1 = a.castToMDerived1()
@unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount")
def testCastFromMDerived2ToBases(self):
- '''MDerived2 is casted by C++ to its parents and the binding must return the MDerived2 wrapper.'''
+ '''MDerived2 is casted by C++ to its parents and the binding must
+ return the MDerived2 wrapper.'''
a = MDerived2()
refcnt = sys.getrefcount(a)
b3 = a.castToBase3()
@unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount")
def testCastFromMDerived3ToBases(self):
- '''MDerived3 is casted by C++ to its parents and the binding must return the MDerived3 wrapper.'''
+ '''MDerived3 is casted by C++ to its parents and the binding must
+ return the MDerived3 wrapper.'''
a = MDerived3()
refcnt = sys.getrefcount(a)
md1 = a.castToMDerived1()
@unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount")
def testCastFromMDerived4ToBases(self):
- '''MDerived4 is casted by C++ to its parents and the binding must return the MDerived4 wrapper.'''
+ '''MDerived4 is casted by C++ to its parents and the binding must
+ return the MDerived4 wrapper.'''
a = MDerived4()
refcnt = sys.getrefcount(a)
b3 = a.castToBase3()
@unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount")
def testCastFromMDerived5ToBases(self):
- '''MDerived5 is casted by C++ to its parents and the binding must return the MDerived5 wrapper.'''
+ '''MDerived5 is casted by C++ to its parents and the binding must
+ return the MDerived5 wrapper.'''
a = MDerived5()
refcnt = sys.getrefcount(a)
b3 = a.castToBase3()
@unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount")
def testCastFromMDerived3ToBase3(self):
- '''MDerived3 is casted by C++ to Base3 grandparent using both the inherited and reimplement castToBase3 methods.'''
+ '''MDerived3 is casted by C++ to Base3 grandparent using both the inherited
+ and reimplement castToBase3 methods.'''
a = MDerived3()
refcnt = sys.getrefcount(a)
b3_reimplemented = a.castToBase3()
self.assertEqual(a, b3_inherited)
self.assertEqual(sys.getrefcount(a), refcnt + 2)
+
if __name__ == '__main__':
unittest.main()
-
from shiboken_paths import init_paths
init_paths()
-from sample import *
+from sample import SampleNamespace
from shiboken_test_helper import objectFullname
-from shiboken6 import Shiboken
-
from shibokensupport.signature import get_signature
# For tests of invisible namespaces, see
class TestVariablesUnderNamespace(unittest.TestCase):
def testIt(self):
- self.assertEqual(SampleNamespace.variableInNamespace, 42)
+ self.assertEqual(SampleNamespace.variableInNamespace, 42)
class TestClassesUnderNamespace(unittest.TestCase):
def testIt(self):
- c1 = SampleNamespace.SomeClass()
- e1 = SampleNamespace.SomeClass.ProtectedEnum()
- c2 = SampleNamespace.SomeClass.SomeInnerClass()
- e2 = SampleNamespace.SomeClass.SomeInnerClass.ProtectedEnum()
- c3 = SampleNamespace.SomeClass.SomeInnerClass.OkThisIsRecursiveEnough()
- e3 = SampleNamespace.SomeClass.SomeInnerClass.OkThisIsRecursiveEnough.NiceEnum(0)
+ c1 = SampleNamespace.SomeClass() # noqa F841
+ e1 = SampleNamespace.SomeClass.ProtectedEnum() # noqa F841
+ c2 = SampleNamespace.SomeClass.SomeInnerClass() # noqa F841
+ e2 = SampleNamespace.SomeClass.SomeInnerClass.ProtectedEnum() # noqa F841
+ c3 = SampleNamespace.SomeClass.SomeInnerClass.OkThisIsRecursiveEnough() # noqa F841
+ e3 = SampleNamespace.SomeClass.SomeInnerClass.OkThisIsRecursiveEnough.NiceEnum(0) # noqa F841
def testFunctionAddedOnNamespace(self):
res = SampleNamespace.ImInsideANamespace(2, 2)
def testTpNames(self):
self.assertEqual(str(SampleNamespace.SomeClass),
- "<class 'sample.SampleNamespace.SomeClass'>")
+ "<class 'sample.SampleNamespace.SomeClass'>")
self.assertEqual(str(SampleNamespace.SomeClass.ProtectedEnum),
- "<enum 'ProtectedEnum'>")
+ "<enum 'ProtectedEnum'>")
self.assertEqual(str(SampleNamespace.SomeClass.SomeInnerClass.ProtectedEnum),
- "<enum 'ProtectedEnum'>")
+ "<enum 'ProtectedEnum'>")
self.assertEqual(str(SampleNamespace.SomeClass.SomeInnerClass.OkThisIsRecursiveEnough),
- "<class 'sample.SampleNamespace.SomeClass.SomeInnerClass.OkThisIsRecursiveEnough'>")
- self.assertEqual(str(SampleNamespace.SomeClass.SomeInnerClass.OkThisIsRecursiveEnough.NiceEnum),
- "<enum 'NiceEnum'>")
+ "<class 'sample.SampleNamespace.SomeClass.SomeInnerClass.OkThisIsRecursiveEnough'>") # noqa: E501
+ self.assertEqual(str(SampleNamespace.SomeClass.SomeInnerClass.OkThisIsRecursiveEnough.NiceEnum), # noqa: E501
+ "<enum 'NiceEnum'>")
# Test if enum inside of class is correct represented
- self.assertEqual(objectFullname(get_signature(SampleNamespace.enumInEnumOut).parameters['in_'].annotation),
- "sample.SampleNamespace.InValue")
- self.assertEqual(objectFullname(get_signature(SampleNamespace.enumAsInt).parameters['value'].annotation),
- "sample.SampleNamespace.SomeClass.PublicScopedEnum")
-
+ an = objectFullname(get_signature(SampleNamespace.enumInEnumOut).parameters['in_'].annotation) # noqa: E501
+ self.assertEqual(an, "sample.SampleNamespace.InValue")
+ an = objectFullname(get_signature(SampleNamespace.enumAsInt).parameters['value'].annotation)
+ self.assertEqual(an, "sample.SampleNamespace.SomeClass.PublicScopedEnum")
def testInlineNamespaces(self):
cls = SampleNamespace.ClassWithinInlineNamespace()
from shiboken_paths import init_paths
init_paths()
-from sample import *
+from sample import Point
class TestNewDivision(unittest.TestCase):
def testIt(self):
p = Point(4, 4)
- p2 = p/2
+ p2 = p / 2
self.assertEqual(p2, Point(2, 2))
+
if __name__ == "__main__":
unittest.main()
-
from sample import NonDefaultCtor
+
class DerivedNonDefaultCtor (NonDefaultCtor):
def returnMyselfVirtual(self):
- return NonDefaultCtor(self.value()+1)
+ return NonDefaultCtor(self.value() + 1)
+
class AnotherDerivedNonDefaultCtor (NonDefaultCtor):
def __init__(self, some_string):
pass
+
class NonDefaultCtorTest(unittest.TestCase):
def testNonDefaultCtor(self):
self.assertEqual(c.callReturnMyselfVirtual().value(), 4)
def testCtorOverload(self):
- c = AnotherDerivedNonDefaultCtor("testing")
+ c = AnotherDerivedNonDefaultCtor("testing") # noqa: F841
+
if __name__ == '__main__':
unittest.main()
-
from sample import IntArray2, IntArray3
+
class NonTypeTemplateTest(unittest.TestCase):
def testNonTypeTemplate(self):
def testArrayInitializer(self):
if not hasNumPy:
return
- array3 = IntArray3(numpy.array([1, 2, 3], dtype = 'int32'))
+ array3 = IntArray3(numpy.array([1, 2, 3], dtype='int32'))
self.assertEqual(array3.sum(), 6)
from sample import Color, Brush
+
class TestNonZeroOperator(unittest.TestCase):
def testColor(self):
"""Color has a Qt-style isNull()"""
from sample import SizeF
+
class NumericalTypedefTest(unittest.TestCase):
def testNumericalTypedefExact(self):
self.assertEqual(SizeF.passTypedefOfUnsignedShort(321), 321)
self.assertNotEqual(SizeF.passTypedefOfUnsignedShort(123), 0)
+
if __name__ == '__main__':
unittest.main()
if bool(sysconfig.get_config_var('Py_DEBUG')):
sys.exit(0)
import numpy
-except:
+except: # noqa: E722
sys.exit(0)
import os
init_paths()
from sample import PointF
+
class TestNumpyTypes(unittest.TestCase):
def testNumpyConverted(self):
self.assertAlmostEqual(p.x(), x)
self.assertAlmostEqual(p.y(), y)
+
if __name__ == "__main__":
unittest.main()
-
def testNextInFocusChainCycle(self):
parent = ObjectType()
child = ObjectType(parent)
- next_focus = child.nextInFocusChain()
+ next_focus = child.nextInFocusChain() # noqa: F841
Shiboken.invalidate(parent)
with self.assertRaises(AttributeError):
o.typo
+
if __name__ == '__main__':
unittest.main()
from sample import ObjectType
+
class NamedArgsTest(unittest.TestCase):
def testOneArgument(self):
o.setObjectNameWithSize(size=6, name="pyside")
self.assertEqual(o.objectName(), "pyside")
-
def testUseDefaultValues(self):
o = ObjectType()
o.setObjectNameWithSize(size=3)
- self.assertEqual(o.objectName(), "<un") # use name='unknown' default argument
+ self.assertEqual(o.objectName(), "<un") # use name='unknown' default argument
o.setObjectSplittedName("")
- self.assertEqual(o.objectName(), "<unknown>") # user prefix='<unk' and suffix='nown>'
-
+ self.assertEqual(o.objectName(), "<unknown>") # user prefix='<unk' and suffix='nown>'
if __name__ == '__main__':
unittest.main()
-
from shiboken_paths import init_paths
init_paths()
-from sample import *
+from sample import ObjectTypeByValue
class ObjectTypeByValueTest (unittest.TestCase):
# just to make sure it will segfault
obj.prop.protectedValueTypeProperty.setY(2.0)
+
if __name__ == "__main__":
unittest.main()
from shiboken_paths import init_paths
init_paths()
-from sample import *
+from sample import ObjectType, ObjectTypeLayout
class ObjectTypeLayoutTest(unittest.TestCase):
@unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount")
def testOwnershipOverride(self):
- l = ObjectTypeLayout()
+ lt = ObjectTypeLayout()
- o1 = ObjectType(l)
+ o1 = ObjectType(lt)
o1.setObjectName('o1')
self.assertEqual(sys.getrefcount(o1), 3)
- l.takeChild('o1')
+ lt.takeChild('o1')
self.assertEqual(sys.getrefcount(o1), 2)
-
def testSetNullLayout(self):
'''ObjectType.setLayout(0).'''
o2 = ObjectType()
self.assertEqual(c3.parent(), None)
p1.setLayout(layout)
- del p1 # This must kill c1, c2 and c3
+ del p1 # This must kill c1, c2 and c3
# PYSIDE-535: Need to collect garbage in PyPy to trigger deletion
gc.collect()
self.assertEqual(c3.parent(), None)
p1.setLayout(layout)
- del p1 # This must kill c1, c2 and c3
+ del p1 # This must kill c1, c2 and c3
# PYSIDE-535: Need to collect garbage in PyPy to trigger deletion
gc.collect()
self.assertRaises(RuntimeError, layout.objectName)
def testObjectTypeLayoutTransference(self):
- '''Transfer a layout from one ObjectType to another, so that all the items in the layout get reparented.'''
+ '''Transfer a layout from one ObjectType to another, so that all the items in
+ the layout get reparented.'''
p1 = ObjectType()
p2 = ObjectType()
c1 = ObjectType()
self.assertRaises(RuntimeError, l2.objectName)
def testObjectTypeLayoutInsideAnotherLayoutAndEveryoneCreatedInCpp(self):
- '''Adds one ObjectTypeLayout to another and sets the parent to an ObjectType. All the objects are created in C++.'''
+ '''Adds one ObjectTypeLayout to another and sets the parent to an ObjectType.
+ All the objects are created in C++.'''
p1 = ObjectType.create()
l1 = ObjectTypeLayout.create()
self.assertRaises(RuntimeError, l2.objectName)
def testTransferNestedLayoutsBetweenObjects(self):
- '''Adds one ObjectTypeLayout to another, sets the parent to an ObjectType and then transfer it to another object.'''
+ '''Adds one ObjectTypeLayout to another, sets the parent to an ObjectType
+ and then transfer it to another object.'''
p1 = ObjectType()
p2 = ObjectType()
self.assertRaises(RuntimeError, l2.objectName)
def testTransferNestedLayoutsBetweenObjectsAndEveryoneCreatedInCpp(self):
- '''Adds one ObjectTypeLayout to another, sets the parent to an ObjectType and then transfer it to another object.
- All the objects are created in C++.'''
+ '''Adds one ObjectTypeLayout to another, sets the parent to an ObjectType and then
+ transfer it to another object. All the objects are created in C++.'''
p1 = ObjectType.create()
p2 = ObjectType.create()
self.assertRaises(RuntimeError, l1.objectName)
self.assertRaises(RuntimeError, l2.objectName)
+
if __name__ == '__main__':
unittest.main()
-
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
from shiboken_paths import init_paths
init_paths()
-from sample import *
+from sample import ObjectTypeOperators
+
class ObjectTypeOperatorsTest(unittest.TestCase):
def testPointerOpeators(self):
a = ObjectTypeOperators("a")
- b = ObjectTypeOperators("b")
+ b = ObjectTypeOperators("b") # noqa: F841
self.assertEqual(a + "bc", "abc")
self.assertEqual("bc" + a, "bca")
self.assertEqual("a", a)
a = ObjectTypeOperators("a")
self.assertNotEqual(a, "b")
+
if __name__ == '__main__':
unittest.main()
init_paths()
from sample import ObjectTypeHolder
+
class TestObjectTypeReferenceAsVirtualMethodArgument(unittest.TestCase):
def testBasic(self):
holder = Holder('TheObjectFromC++')
self.assertEqual(holder.callPassObjectTypeAsReference(), 'ThisIsTheObjectFromC++')
+
if __name__ == '__main__':
unittest.main()
from sample import OddBoolUser, ComparisonTester, SpaceshipComparisonTester
+
class DerivedOddBoolUser (OddBoolUser):
def returnMyselfVirtual(self):
return OddBoolUser()
pass
+
class OddBoolTest(unittest.TestCase):
def testOddBoolUser(self):
self.assertEqual(obuTrue.oddBool(), True)
self.assertEqual(obuTrue.callInvertedOddBool(), False)
- self.assertEqual(obuTrue.oddBool() == True, True)
- self.assertEqual(False == obuFalse.oddBool(), True)
- self.assertEqual(obuTrue.oddBool() == obuFalse.oddBool(), False)
+ self.assertTrue(obuTrue.oddBool())
+ self.assertFalse(obuFalse.oddBool())
+ self.assertTrue(obuTrue.oddBool() != obuFalse.oddBool())
- self.assertEqual(obuFalse.oddBool() != True, True)
- self.assertEqual(True != obuFalse.oddBool(), True)
- self.assertEqual(obuTrue.oddBool() != obuFalse.oddBool(), True)
+ self.assertFalse(obuFalse.oddBool())
+ self.assertFalse(obuFalse.oddBool())
+ self.assertTrue(obuTrue.oddBool() != obuFalse.oddBool())
def testVirtuals(self):
dobu = DerivedOddBoolUser()
from sample import OnlyCopy, FriendOfOnlyCopy
+
class ClassWithOnlyCopyCtorTest(unittest.TestCase):
def testGetOne(self):
obj = FriendOfOnlyCopy.createOnlyCopy(123)
obj = FriendOfOnlyCopy.createOnlyCopy(123)
self.assertEqual(obj.value(), OnlyCopy.getValueFromReference(obj))
+
if __name__ == '__main__':
unittest.main()
from shiboken_paths import init_paths
init_paths()
-from sample import *
+from sample import (Point, doubleLongLong, doubleShort, doubleUnsignedInt,
+ doubleUnsignedLongLong)
class OverflowTest(unittest.TestCase):
return super().assertRaises(*args, **kwds)
def testUnsignedInt(self):
- '''C++ function receives an unsigned int argument and raise OverflowError if the value is negative.'''
+ '''C++ function receives an unsigned int argument and raise OverflowError
+ if the value is negative.'''
val = 100
self.assertEqual(doubleUnsignedInt(val), 2 * val)
val *= -1
self.assertRaises(OverflowError, doubleUnsignedInt, val)
def testLongLong(self):
- '''C++ function receives an long long argument and raise OverflowError if the value is negative.'''
+ '''C++ function receives an long long argument and raise OverflowError
+ if the value is negative.'''
val = 100
self.assertEqual(doubleLongLong(val), 2 * val)
val = int(100)
self.assertRaises(OverflowError, doubleLongLong, val)
def testUnsignedLongLong(self):
- '''C++ function receives an unsigned long long argument and raise OverflowError if the value is negative.'''
+ '''C++ function receives an unsigned long long argument and raise OverflowError
+ if the value is negative.'''
val = 100
self.assertEqual(doubleUnsignedLongLong(val), 2 * val)
val = int(100)
def testShortOverflow(self):
'''Calls function with short parameter using an overflowing value.'''
doubleShort(-3)
- self.assertRaises(OverflowError, doubleShort, 0xFFFF*-1)
+ self.assertRaises(OverflowError, doubleShort, 0xFFFF * -1)
self.assertRaises(OverflowError, doubleShort, 0xFFFF + 1)
def testOverflowOnCtor(self):
'''Calls object ctor with int parameter using overflowing values.'''
self.assertRaises(OverflowError, Point, 42415335332353253, 42415335332353253)
+
if __name__ == '__main__':
unittest.main()
-
from shiboken_paths import init_paths
init_paths()
-from sample import *
+from sample import (CustomOverloadSequence, ImplicitBase, ImplicitConv,
+ ImplicitTarget, SortedOverload)
+
class Dummy(object):
pass
+
class SimpleOverloadSorting(unittest.TestCase):
def setUp(self):
'''Deep Overload - (int, ImplicitBase *)'''
self.assertEqual(self.obj.overloadDeep(1, ImplicitBase()), "ImplicitBase")
+
class EnumOverIntSorting(unittest.TestCase):
def testEnumOverInt(self):
ic = ImplicitConv(ImplicitConv.CtorTwo)
try:
func(*arguments)
return False
- except Exception as err:
- if type(err) != TypeError:
- return False
- if not errorMsg in str(err):
- return False
+ except TypeError as err:
+ return errorMsg in str(err)
+ except Exception:
+ return False
return True
def testAcceptSequencePyObject(self):
# Overload.acceptSequence(void*)
overload = Overload()
+
class Foo(object):
pass
+
foo = Foo()
self.assertEqual(overload.acceptSequence(foo), Overload.Function5)
if __name__ == '__main__':
unittest.main()
-
overload = Overload()
self.assertEqual(overload.strBufferOverloads(bytes('', "UTF-8"), 0), Overload.Function1)
+
if __name__ == '__main__':
unittest.main()
-
from sample import Polygon, Point
+
class WrapperValidityOfArgumentsTest(unittest.TestCase):
'''Wrapper validity tests for arguments.'''
self.assertRaises(RuntimeError, Polygon.doublePolygonScale, poly)
def testInvalidArgumentToConstructor(self):
- '''Call to constructor using invalidated Python wrapper as argument should raise RuntimeError.'''
+ '''Call to constructor using invalidated Python wrapper as argument
+ should raise RuntimeError.'''
pt = Point(1, 2)
Polygon.stealOwnershipFromPython(pt)
self.assertRaises(RuntimeError, Polygon, pt)
def testInvalidArgumentWithImplicitConversion(self):
- '''Call to method using invalidated Python wrapper to be implicitly converted should raise RuntimeError.'''
+ '''Call to method using invalidated Python wrapper to be implicitly converted
+ should raise RuntimeError.'''
pt = Point(1, 2)
Polygon.stealOwnershipFromPython(pt)
self.assertRaises(RuntimeError, Polygon.doublePolygonScale, pt)
+
if __name__ == '__main__':
unittest.main()
-
self.assertRaises(RuntimeError, child.objectName)
self.assertEqual(parent.objectName(), 'parent')
+
if __name__ == '__main__':
unittest.main()
new_child = parent.children()[0]
self.assertEqual(new_child.objectName(), name)
+
if __name__ == '__main__':
unittest.main()
# PYSIDE-535: Need to collect garbage in PyPy to trigger deletion
gc.collect()
self.assertRaises(RuntimeError, child.objectName)
- self.assertEqual(sys.getrefcount(child), refcount_before-1)
+ self.assertEqual(sys.getrefcount(child), refcount_before - 1)
@unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount")
def testParentDestructorMultipleChildren(self):
ObjectType.__init__(self)
self.type_of_last_event = None
self.last_event = None
+
def event(self, event):
self.last_event = event
self.type_of_last_event = event.eventType()
return True
+
class MyObjectType (ObjectType):
def __init__(self):
super(MyObjectType, self).__init__()
self.callInvalidateEvent(ev)
try:
ev.eventType()
- except:
+ except: # noqa: E722
self.fail = True
raise
return True
def invalidateEvent(self, ev):
pass
+
class ExtObjectTypeDerived(ObjectTypeDerived):
def __init__(self):
ObjectTypeDerived.__init__(self)
self.type_of_last_event = None
self.last_event = None
+
def event(self, event):
self.last_event = event
self.type_of_last_event = event.eventType()
return True
+
class OwnershipInvalidateAfterUseTest(unittest.TestCase):
'''Ownership tests for cases of invalidation of Python wrapper after use.'''
def testInvalidateAfterUse(self):
- '''In ObjectType.event(Event*) the wrapper object created for Event must me marked as invalid after the method is called.'''
+ '''In ObjectType.event(Event*) the wrapper object created for Event
+ must me marked as invalid after the method is called.'''
eot = ExtObjectType()
eot.causeEvent(Event.SOME_EVENT)
self.assertEqual(eot.type_of_last_event, Event.SOME_EVENT)
self.assertEqual(eot.type_of_last_event, Event.SOME_EVENT)
self.assertRaises(RuntimeError, eot.last_event.eventType)
+
if __name__ == '__main__':
unittest.main()
-
self.assertEqual(child1.objectName(), 'child1')
self.assertRaises(RuntimeError, child2.objectName)
+
if __name__ == '__main__':
unittest.main()
-
from sample import Point, BlackBox
+
class OwnershipInvalidateNonPolymorphicTest(unittest.TestCase):
'''The BlackBox class has cases of ownership transference between Python and C++.'''
p1_ret = bb.retrievePoint(p1_ticket)
self.assertEqual(p1_ret, Point(10, 20))
+
if __name__ == '__main__':
unittest.main()
-
grandchild2.setParent(child2)
bbox = BlackBox()
- bbox.keepObjectType(parent) # Should invalidate the parent
+ bbox.keepObjectType(parent) # Should invalidate the parent
self.assertRaises(RuntimeError, parent.objectName)
# some children still valid they are wrapper classes
self.assertEqual(grandchild1.objectName(), "grandchild1")
self.assertRaises(RuntimeError, grandchild2.objectName)
+
if __name__ == '__main__':
unittest.main()
-
from sample import ObjectType
+
class ExtObjectType(ObjectType):
def __init__(self):
ObjectType.__init__(self)
if __name__ == '__main__':
unittest.main()
-
from sample import ObjectType, BlackBox
+
class BlackBoxTest(unittest.TestCase):
'''The BlackBox class has cases of ownership transference between C++ and Python.'''
o2.setObjectName('object2')
o2_refcnt = sys.getrefcount(o2)
bb = BlackBox()
- o1_ticket = bb.keepObjectType(o1)
+ o1_ticket = bb.keepObjectType(o1) # noqa: F841
o2_ticket = bb.keepObjectType(o2)
self.assertEqual(set(bb.objects()), set([o1, o2]))
self.assertEqual(str(o1.objectName()), 'object1')
self.assertEqual(str(o2.objectName()), 'object2')
- self.assertEqual(sys.getrefcount(o1), o1_refcnt + 1) # PySide give +1 ref to object with c++ ownership
+ # PySide give +1 ref to object with c++ ownership
+ self.assertEqual(sys.getrefcount(o1), o1_refcnt + 1)
self.assertEqual(sys.getrefcount(o2), o2_refcnt + 1)
o2 = bb.retrieveObjectType(o2_ticket)
self.assertEqual(sys.getrefcount(o2), o2_refcnt)
def testBlackBoxReleasingUnknownObjectType(self):
'''Asks BlackBox to release an unknown ObjectType.'''
o1 = ObjectType()
- o2 = ObjectType()
+ o2 = ObjectType() # noqa: F841
bb = BlackBox()
- o1_ticket = bb.keepObjectType(o1)
+ o1_ticket = bb.keepObjectType(o1) # noqa: F841
o3 = bb.retrieveObjectType(-5)
self.assertEqual(o3, None)
'''Ownership transference using a C++ created object.'''
o1 = ObjectType.create()
o1.setObjectName('object1')
- o1_refcnt = sys.getrefcount(o1)
+ o1_refcnt = sys.getrefcount(o1) # noqa: F841
bb = BlackBox()
- o1_ticket = bb.keepObjectType(o1)
+ o1_ticket = bb.keepObjectType(o1) # noqa: F841
self.assertRaises(RuntimeError, o1.objectName)
+
if __name__ == '__main__':
unittest.main()
-
from sample import PairUser
+
class ExtendedPairUser(PairUser):
def __init__(self):
PairUser.__init__(self)
self.create_pair_called = True
return (7, 13)
+
class PairConversionTest(unittest.TestCase):
'''Test case for std::pair container conversions'''
self.assertEqual(cp, (cpx0, cpx1))
def testSumPair(self):
- '''Test method that sums the items of a pair using values of the types expected by C++ (int and double)'''
+ '''Test method that sums the items of a pair using values of the types
+ expected by C++ (int and double)'''
pu = PairUser()
pair = (3, 7.13)
result = pu.sumPair(pair)
self.assertEqual(result, sum(pair))
def testSumPairDifferentTypes(self):
- '''Test method that sums the items of a pair using values of types different from the ones expected by C++ (int and double)'''
+ '''Test method that sums the items of a pair using values of types different
+ from the ones expected by C++ (int and double)'''
pu = PairUser()
pair = (3.3, 7)
result = pu.sumPair(pair)
self.assertEqual(result, pair)
def testConversionInBothDirectionsWithSimilarContainer(self):
- '''Test converting a list, instead of the expected tuple, from Python to C++ and the other way around.'''
+ '''Test converting a list, instead of the expected tuple, from Python to C++
+ and the other way around.'''
pu = PairUser()
pair = [3, 5]
pu.setPair(pair)
self.assertNotEqual(result, pair)
self.assertEqual(result, tuple(pair))
+
if __name__ == '__main__':
unittest.main()
-
from sample import Color, Pen, SampleNamespace
+
class TestPen(unittest.TestCase):
'''Simple test case for Pen.'''
from sample import Point
+
class PointTest(unittest.TestCase):
'''Test case for Point class, including operator overloads.'''
expected = Point((pt1.x() + pt2.x()) / 2.0, (pt1.y() + pt2.y()) / 2.0)
self.assertEqual(pt1.midpoint(pt2), expected)
+
if __name__ == '__main__':
unittest.main()
from sample import PointerHolder
+
class TestPointerHolder(unittest.TestCase):
- '''Test cases for a class that holds an arbitraty pointer and is modified to hold an PyObject.'''
+ '''Test cases for a class that holds an arbitraty pointer and
+ is modified to hold an PyObject.'''
def testStoringAndRetrievingPointer(self):
ph = PointerHolder('Hello')
a = (1, 2, 3)
refcnt = sys.getrefcount(a)
ph = PointerHolder(a)
- ptr = ph.pointer()
+ ptr = ph.pointer() # noqa: F841
self.assertEqual(sys.getrefcount(a), refcnt + 1)
+
if __name__ == '__main__':
unittest.main()
-
init_paths()
from sample import IntArray2, VirtualMethods
-import shiboken6
from shibokensupport.signature import get_signature
import typing
from sample import PointF
+
class PointFTest(unittest.TestCase):
'''Test case for PointF class, including operator overloads.'''
expected = PointF((pt1.x() + pt2.x()) / 2.0, (pt1.y() + pt2.y()) / 2.0)
self.assertEqual(pt1.midpoint(pt2), expected)
+
if __name__ == '__main__':
unittest.main()
init_paths()
import sample
+
class PrimitiveReferenceArgumentTest(unittest.TestCase):
def testIntReferenceArgument(self):
self.assertNotEqual(sample.acceptOddBoolReference(True), False)
self.assertNotEqual(sample.acceptOddBoolReference(False), True)
+
if __name__ == '__main__':
unittest.main()
self.assertEqual(pd3.instanceCalls(), calls + 2)
self.assertEqual(sys.getrefcount(pd3), refcnt)
+
if __name__ == '__main__':
unittest.main()
-
self.assertLess(abs(before - after), 5)
+
if __name__ == '__main__':
unittest.main()
-
from sample import cacheSize
from sample import ProtectedNonPolymorphic, ProtectedVirtualDestructor
-from sample import ProtectedPolymorphic, ProtectedPolymorphicDaughter, ProtectedPolymorphicGrandDaughter
+from sample import (ProtectedPolymorphic, ProtectedPolymorphicDaughter,
+ ProtectedPolymorphicGrandDaughter)
from sample import createProtectedProperty, ProtectedProperty, ProtectedEnumClass
from sample import PrivateDtor
from sample import Event, ObjectType, Point
+
class ExtendedProtectedPolymorphic(ProtectedPolymorphic):
def __init__(self, name):
ProtectedPolymorphic.__init__(self, name)
self.protectedName_called = False
+
def protectedName(self):
self.protectedName_called = True
self._name = 'Extended' + ProtectedPolymorphic.protectedName(self)
return self._name
+
class ExtendedProtectedPolymorphicDaughter(ProtectedPolymorphicDaughter):
def __init__(self, name):
self.protectedName_called = False
ProtectedPolymorphicDaughter.__init__(self, name)
+
def protectedName(self):
self.protectedName_called = True
self._name = 'ExtendedDaughter' + ProtectedPolymorphicDaughter.protectedName(self)
return self._name
+
class ExtendedProtectedPolymorphicGrandDaughter(ProtectedPolymorphicGrandDaughter):
def __init__(self, name):
self.protectedName_called = False
ProtectedPolymorphicGrandDaughter.__init__(self, name)
+
def protectedName(self):
self.protectedName_called = True
self._name = 'ExtendedGrandDaughter' + ProtectedPolymorphicGrandDaughter.protectedName(self)
return self._name
+
class ExtendedProtectedVirtualDestructor(ProtectedVirtualDestructor):
def __init__(self):
ProtectedVirtualDestructor.__init__(self)
+
class ProtectedNonPolymorphicTest(unittest.TestCase):
'''Test cases for protected method in a class without virtual methods.'''
self.assertEqual(p.dataTypeName(1), 'integer')
self.assertEqual(p.dataTypeName(Point(1, 2)), 'pointer')
+
class ProtectedPolymorphicTest(unittest.TestCase):
'''Test cases for protected method in a class with virtual methods.'''
self.assertTrue(p.protectedName_called)
self.assertEqual(p.protectedName(), name)
self.assertEqual(ProtectedPolymorphic.protectedName(p), original_name)
+
+
class ProtectedPolymorphicDaugherTest(unittest.TestCase):
'''Test cases for protected method in a class inheriting for a class with virtual methods.'''
self.assertEqual(p.protectedName(), name)
self.assertEqual(ProtectedPolymorphicGrandDaughter.protectedName(p), original_name)
+
class ProtectedVirtualDtorTest(unittest.TestCase):
'''Test cases for protected virtual destructor.'''
class ExtendedProtectedEnumClass(ProtectedEnumClass):
def __init__(self):
ProtectedEnumClass.__init__(self)
+
def protectedEnumMethod(self, value):
if value == ProtectedEnumClass.ProtectedItem0:
return ProtectedEnumClass.ProtectedItem1
return ProtectedEnumClass.ProtectedItem0
+
def publicEnumMethod(self, value):
if value == ProtectedEnumClass.PublicItem0:
return ProtectedEnumClass.PublicItem1
return ProtectedEnumClass.PublicItem0
+
class ProtectedEnumTest(unittest.TestCase):
'''Test cases for protected enum.'''
self.assertEqual(type(ProtectedEnumClass.ProtectedItem0), ProtectedEnumClass.ProtectedEnum)
- self.assertEqual(obj.protectedEnumMethod(ProtectedEnumClass.ProtectedItem0), ProtectedEnumClass.ProtectedItem0)
- self.assertEqual(obj.protectedEnumMethod(ProtectedEnumClass.ProtectedItem1), ProtectedEnumClass.ProtectedItem1)
-
- self.assertEqual(obj.callProtectedEnumMethod(ProtectedEnumClass.ProtectedItem0), ProtectedEnumClass.ProtectedItem0)
- self.assertEqual(obj.callProtectedEnumMethod(ProtectedEnumClass.ProtectedItem1), ProtectedEnumClass.ProtectedItem1)
+ self.assertEqual(obj.protectedEnumMethod(ProtectedEnumClass.ProtectedItem0),
+ ProtectedEnumClass.ProtectedItem0)
+ self.assertEqual(obj.protectedEnumMethod(ProtectedEnumClass.ProtectedItem1),
+ ProtectedEnumClass.ProtectedItem1)
+ self.assertEqual(obj.callProtectedEnumMethod(ProtectedEnumClass.ProtectedItem0),
+ ProtectedEnumClass.ProtectedItem0)
+ self.assertEqual(obj.callProtectedEnumMethod(ProtectedEnumClass.ProtectedItem1),
+ ProtectedEnumClass.ProtectedItem1)
def testProtectedMethodWithPublicEnumArgument(self):
'''Calls protected method with public enum argument.'''
obj = ProtectedEnumClass()
- self.assertEqual(obj.publicEnumMethod(ProtectedEnumClass.PublicItem0), ProtectedEnumClass.PublicItem0)
- self.assertEqual(obj.publicEnumMethod(ProtectedEnumClass.PublicItem1), ProtectedEnumClass.PublicItem1)
+ self.assertEqual(obj.publicEnumMethod(ProtectedEnumClass.PublicItem0),
+ ProtectedEnumClass.PublicItem0)
+ self.assertEqual(obj.publicEnumMethod(ProtectedEnumClass.PublicItem1),
+ ProtectedEnumClass.PublicItem1)
- self.assertEqual(obj.callPublicEnumMethod(ProtectedEnumClass.PublicItem0), ProtectedEnumClass.PublicItem0)
- self.assertEqual(obj.callPublicEnumMethod(ProtectedEnumClass.PublicItem1), ProtectedEnumClass.PublicItem1)
+ self.assertEqual(obj.callPublicEnumMethod(ProtectedEnumClass.PublicItem0),
+ ProtectedEnumClass.PublicItem0)
+ self.assertEqual(obj.callPublicEnumMethod(ProtectedEnumClass.PublicItem1),
+ ProtectedEnumClass.PublicItem1)
def testOverriddenProtectedMethodWithProtectedEnumArgument(self):
'''Calls overridden protected method with protected enum argument.'''
obj = ExtendedProtectedEnumClass()
- self.assertEqual(obj.protectedEnumMethod(ProtectedEnumClass.ProtectedItem0), ProtectedEnumClass.ProtectedItem1)
- self.assertEqual(obj.protectedEnumMethod(ProtectedEnumClass.ProtectedItem1), ProtectedEnumClass.ProtectedItem0)
+ self.assertEqual(obj.protectedEnumMethod(ProtectedEnumClass.ProtectedItem0),
+ ProtectedEnumClass.ProtectedItem1)
+ self.assertEqual(obj.protectedEnumMethod(ProtectedEnumClass.ProtectedItem1),
+ ProtectedEnumClass.ProtectedItem0)
- self.assertEqual(ProtectedEnumClass.protectedEnumMethod(obj, ProtectedEnumClass.ProtectedItem0), ProtectedEnumClass.ProtectedItem0)
- self.assertEqual(ProtectedEnumClass.protectedEnumMethod(obj, ProtectedEnumClass.ProtectedItem1), ProtectedEnumClass.ProtectedItem1)
+ self.assertEqual(ProtectedEnumClass.protectedEnumMethod(obj, ProtectedEnumClass.ProtectedItem0), # noqa: E501
+ ProtectedEnumClass.ProtectedItem0)
+ self.assertEqual(ProtectedEnumClass.protectedEnumMethod(obj,
+ ProtectedEnumClass.ProtectedItem1), ProtectedEnumClass.ProtectedItem1)
- self.assertEqual(obj.callProtectedEnumMethod(ProtectedEnumClass.ProtectedItem0), ProtectedEnumClass.ProtectedItem1)
- self.assertEqual(obj.callProtectedEnumMethod(ProtectedEnumClass.ProtectedItem1), ProtectedEnumClass.ProtectedItem0)
+ self.assertEqual(obj.callProtectedEnumMethod(ProtectedEnumClass.ProtectedItem0),
+ ProtectedEnumClass.ProtectedItem1)
+ self.assertEqual(obj.callProtectedEnumMethod(ProtectedEnumClass.ProtectedItem1),
+ ProtectedEnumClass.ProtectedItem0)
def testOverriddenProtectedMethodWithPublicEnumArgument(self):
'''Calls overridden protected method with public enum argument.'''
obj = ExtendedProtectedEnumClass()
- self.assertEqual(obj.publicEnumMethod(ProtectedEnumClass.PublicItem0), ProtectedEnumClass.PublicItem1)
- self.assertEqual(obj.publicEnumMethod(ProtectedEnumClass.PublicItem1), ProtectedEnumClass.PublicItem0)
+ self.assertEqual(obj.publicEnumMethod(ProtectedEnumClass.PublicItem0),
+ ProtectedEnumClass.PublicItem1)
+ self.assertEqual(obj.publicEnumMethod(ProtectedEnumClass.PublicItem1),
+ ProtectedEnumClass.PublicItem0)
- self.assertEqual(ProtectedEnumClass.publicEnumMethod(obj, ProtectedEnumClass.PublicItem0), ProtectedEnumClass.PublicItem0)
- self.assertEqual(ProtectedEnumClass.publicEnumMethod(obj, ProtectedEnumClass.PublicItem1), ProtectedEnumClass.PublicItem1)
+ self.assertEqual(ProtectedEnumClass.publicEnumMethod(obj, ProtectedEnumClass.PublicItem0),
+ ProtectedEnumClass.PublicItem0)
+ self.assertEqual(ProtectedEnumClass.publicEnumMethod(obj, ProtectedEnumClass.PublicItem1),
+ ProtectedEnumClass.PublicItem1)
- self.assertEqual(obj.callPublicEnumMethod(ProtectedEnumClass.PublicItem0), ProtectedEnumClass.PublicItem1)
- self.assertEqual(obj.callPublicEnumMethod(ProtectedEnumClass.PublicItem1), ProtectedEnumClass.PublicItem0)
+ self.assertEqual(obj.callPublicEnumMethod(ProtectedEnumClass.PublicItem0),
+ ProtectedEnumClass.PublicItem1)
+ self.assertEqual(obj.callPublicEnumMethod(ProtectedEnumClass.PublicItem1),
+ ProtectedEnumClass.PublicItem0)
class ProtectedPropertyTest(unittest.TestCase):
self.assertEqual(obj.instanceCalls(), 2)
self.assertEqual(obj.instanceCalls(), obj.protectedInstanceCalls())
+
if __name__ == '__main__':
unittest.main()
-
init_paths()
import sample
+
class PStrListTest(unittest.TestCase):
def testPStrList(self):
lst = sample.createListOfPStr(a, b)
self.assertEqual(lst, [a, b])
+
if __name__ == '__main__':
unittest.main()
from sample import Point
+
class PyStrTest(unittest.TestCase):
'''Test case for definition of __str__ method.'''
pt = Point(5, 2)
self.assertEqual(str(pt), 'Point(5.0, 2.0)')
+
if __name__ == '__main__':
unittest.main()
-
def run(self):
while self.runs < self.max_runs:
- value = int(random()*10) % 10
+ value = int(random() * 10) % 10
self.bucket.push(value)
self.production_list.append(value)
logging.debug(f'PRODUCER - pushed {value}')
logging.debug('CONSUMER - empty bucket')
time.sleep(0.01)
+
class ProducerConsumer(unittest.TestCase):
'''Basic test case for producer-consumer QThread'''
self.assertEqual(prod.production_list, cons.consumption_list)
-
-
-
if __name__ == '__main__':
unittest.main()
from sample import countCharacters
+
class ReceiveNullCStringTest(unittest.TestCase):
- '''Test case for a function that could receive a NULL pointer in a '[const] char*' parameter.'''
+ '''Test case for a function that could receive a NULL pointer in a '[const] char*'
+ parameter.'''
def testBasic(self):
'''The test function should be working for the basic cases.'''
'''The test function returns '-1' when receives a None value instead of a string.'''
self.assertEqual(countCharacters(None), -1)
+
if __name__ == '__main__':
unittest.main()
-
from shiboken_paths import init_paths
init_paths()
-from sample import *
+from sample import Reference, Str
+
class ExtendedReference(Reference):
def __init__(self):
def testCantSegFaultWhenReceiveNone(self):
'''do not segfault when receiving None as argument.'''
s = Str()
- self.assertTrue(None == s)
+ self.assertFalse(bool(s))
def testMethodThatReceivesConstReference(self):
'''Test a method that receives a const reference to an object as argument.'''
self.assertEqual(Reference.usesConstReference(r), objId)
def testModificationOfReference(self):
- '''Tests if the identity of a reference argument is preserved when passing it to be altered in C++.'''
+ '''Tests if the identity of a reference argument is preserved when passing
+ it to be altered in C++.'''
objId = 123
r1 = Reference(objId)
r1.alterReferenceIdVirtual(r1)
self.assertEqual(r1.objId(), objId * Reference.multiplier())
def testModificationOfReferenceCallingAVirtualIndirectly(self):
- '''Tests if the identity of a reference argument is preserved when passing it to be altered in C++ through a method that calls a virtual method.'''
+ '''Tests if the identity of a reference argument is preserved when passing it
+ to be altered in C++ through a method that calls a virtual method.'''
objId = 123
r1 = Reference(objId)
r1.callAlterReferenceIdVirtual(r1)
self.assertEqual(r1.objId(), objId * Reference.multiplier())
def testModificationOfReferenceCallingAReimplementedVirtualIndirectly(self):
- '''Test if a Python override of a virtual method with a reference parameter called from C++ alters the argument properly.'''
+ '''Test if a Python override of a virtual method with a reference parameter
+ called from C++ alters the argument properly.'''
objId = 123
r = Reference(objId)
er = ExtendedReference()
- result = er.callAlterReferenceIdVirtual(r)
+ result = er.callAlterReferenceIdVirtual(r) # noqa: F841
self.assertEqual(r.objId(), objId * er.multiplier)
def testReimplementedVirtualMethodCallWithReferenceParameter(self):
- '''Test if a Python override of a virtual method with a reference parameter is correctly called from C++.'''
+ '''Test if a Python override of a virtual method with a reference parameter
+ is correctly called from C++.'''
inc = 9
objId = 123
r = Reference(objId)
self.assertEqual(result, objId + inc + er.reference_inc)
def testReimplementedVirtualMethodCallWithConstReferenceParameter(self):
- '''Test if a Python override of a virtual method with a const reference parameter is correctly called from C++.'''
+ '''Test if a Python override of a virtual method with a const reference
+ parameter is correctly called from C++.'''
inc = 9
objId = 123
r = Reference(objId)
result = er.callUsesConstReferenceVirtual(r, inc)
self.assertEqual(result, objId + inc + er.const_reference_inc)
+
if __name__ == '__main__':
unittest.main()
-
from sample import VirtualMethods, Str
+
class ExtendedVirtualMethods(VirtualMethods):
def __init__(self):
VirtualMethods.__init__(self)
self.assertTrue(ok)
self.assertEqual(string, Str(obj.prefix + 'foo'))
+
if __name__ == '__main__':
unittest.main()
-
from sample import RenamedValue, RenamedUser
-from shiboken6 import Shiboken
-
from shibokensupport.signature import get_signature
actual_signature))
-
if __name__ == '__main__':
unittest.main()
from shiboken_paths import init_paths
init_paths()
-from sample import returnNullPrimitivePointer, returnNullValueTypePointer, returnNullObjectTypePointer
+from sample import (returnNullPrimitivePointer, returnNullValueTypePointer,
+ returnNullObjectTypePointer)
+
class ReturnNullTest(unittest.TestCase):
'''Test case for functions that could return a NULL pointer.'''
o = returnNullValueTypePointer()
self.assertEqual(o, None)
+
if __name__ == '__main__':
unittest.main()
-
from shiboken_paths import init_paths
init_paths()
-from sample import *
+from sample import Expression
+
class TestRichCompare(unittest.TestCase):
d = a + c < b + a
self.assertEqual(d.toString(), "((2+(2+3))<(3+2))")
+
if __name__ == '__main__':
unittest.main()
import sample
+
class ModuleTest(unittest.TestCase):
'''Test case for module and global functions'''
sample.testNullPtrT(None)
self.assertRaises(TypeError, sample.testNullPtrT, 42)
-
def testRValueRefsWithValueTypes(self):
"""Passing value types by rvalue refs: For value types, nothing should
happen since the argument is copied in the call and the copy is
if __name__ == '__main__':
unittest.main()
-
from sample import SimpleFile
+
class SimpleFileTest(unittest.TestCase):
'''Test cases for SimpleFile class.'''
self.assertRaises(IOError, f.open)
self.assertEqual(f.size(), 0)
+
if __name__ == '__main__':
unittest.main()
-
from sample import Size
+
class PointTest(unittest.TestCase):
'''Test case for Size class, including operator overloads.'''
self.assertTrue(s1 > s2)
self.assertFalse(s2 > s1)
+
if __name__ == '__main__':
unittest.main()
-
from sample import SimpleFile
+
class SimpleFile2 (SimpleFile):
def exists(self):
return "Mooo"
+
class SimpleFile3 (SimpleFile):
pass
+
class SimpleFile4 (SimpleFile):
exists = 5
+
class StaticNonStaticMethodsTest(unittest.TestCase):
'''Test cases for overloads involving static and non-static versions of a method.'''
def testDuckPunchingStaticNonStaticMethod(self):
f = SimpleFile(os.fspath(self.existing_filename))
- f.exists = lambda : "Meee"
+ f.exists = lambda: "Meee"
self.assertEqual(f.exists(), "Meee")
+
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-'''Test cases for a method that receives a reference to class that is implicitly convertible from a Python native type.'''
+'''Test cases for a method that receives a reference to class that is implicitly
+ convertible from a Python native type.'''
import os
import sys
from sample import Str
+
class StrTest(unittest.TestCase):
'''Test cases for thr Str class.'''
self.assertEqual(str(s1), 'This is Sparta!')
def testPassPythonTypeImplictlyConvertibleToAClassUsedAsReference(self):
- '''Test passing a Python class implicitly convertible to a wrapped class that is expected to be passed as reference.'''
+ '''Test passing a Python class implicitly convertible to a wrapped class
+ that is expected to be passed as reference.'''
s1 = Str('This is %VAR!').arg('Athens')
self.assertEqual(str(s1), 'This is Athens!')
def testSequenceOperators(self):
s1 = Str("abcdef")
- self.assertEqual(len(s1), 6);
- self.assertEqual(len(Str()), 0);
+ self.assertEqual(len(s1), 6)
+ self.assertEqual(len(Str()), 0)
# getitem
- self.assertEqual(s1[0], "a");
- self.assertEqual(s1[1], "b");
- self.assertEqual(s1[2], "c");
- self.assertEqual(s1[3], "d");
- self.assertEqual(s1[4], "e");
- self.assertEqual(s1[5], "f");
- self.assertEqual(s1[-1], "f");
- self.assertEqual(s1[-2], "e");
+ self.assertEqual(s1[0], "a")
+ self.assertEqual(s1[1], "b")
+ self.assertEqual(s1[2], "c")
+ self.assertEqual(s1[3], "d")
+ self.assertEqual(s1[4], "e")
+ self.assertEqual(s1[5], "f")
+ self.assertEqual(s1[-1], "f")
+ self.assertEqual(s1[-2], "e")
self.assertRaises(TypeError, s1.__getitem__, 6)
# setitem
s1[0] = 'A'
s1[1] = 'B'
- self.assertEqual(s1[0], 'A');
- self.assertEqual(s1[1], 'B');
+ self.assertEqual(s1[0], 'A')
+ self.assertEqual(s1[1], 'B')
self.assertRaises(TypeError, s1.__setitem__(6, 67))
def testReverseOperator(self):
s1 = Str("hello")
- n1 = 2
- self.assertEqual(s1+2, "hello2")
- self.assertEqual(2+s1, "2hello")
+ self.assertEqual(s1 + 2, "hello2")
+ self.assertEqual(2 + s1, "2hello")
def testToIntError(self):
self.assertEqual(Str('Z').toInt(), (0, False))
self.assertEqual(val, int(str(hexa), 16))
self.assertEqual(hexa.toInt(), (0, False))
+
if __name__ == '__main__':
unittest.main()
-
from sample import Str, StrList
+
class StrListTest(unittest.TestCase):
'''Test cases for StrList class that inherits from std::list<Str>.'''
self.assertEqual(len(sl), 2)
self.assertEqual(sl, (Str('Foo'), 'Bar'))
+
if __name__ == '__main__':
unittest.main()
from sample import Photon
'''This tests classes that inherit from template classes,
-simulating a situation found in Qt's phonon module.'''
+ simulating a situation found in Qt's phonon module.'''
+
class TemplateInheritingClassTest(unittest.TestCase):
def testClassBasics(self):
self.assertEqual(obj2, obj2.passPointerThrough(obj2))
self.assertRaises(TypeError, obj1.passPointerThrough, obj2)
+
if __name__ == '__main__':
unittest.main()
'''Test cases for constructor and method signature decisor on Time class.'''
-import sys
import os
import sys
import unittest
from sample import Time, ImplicitConv, ObjectType
+
class TimeTest(unittest.TestCase):
'''Test cases for constructor and method signature decisor on Time class.
The constructor and one method have these signatures: CTORMETHOD() and
py = datetime.time(12, 32, 5)
self.assertNotEqual(time, py)
+
if __name__ == '__main__':
unittest.main()
-
from sample import Point, applyHomogeneousTransform
+
class TransformTest(unittest.TestCase):
'''Test cases for modifying a function with > 9 arguments.'''
r = applyHomogeneousTransform(p, 1, 0, 0, 0, 1, 0, 0, 0, 0)
self.assertTrue(r is None)
+
if __name__ == '__main__':
unittest.main()
import sample
+
class GetPythonTypeByNameTest(unittest.TestCase):
'''Uses an added function with inject code that uses the libshiboken
point.setX(large_int)
self.assertEqual(round(point.x()), large_int)
+ def testUnsignedLongLongAsFloat(self):
+ """PYSIDE-2652: When passing an unsigned long long to a function taking float,
+ an unsigned 64bit conversion should be done."""
+ point = sample.PointF(1, 2)
+ large_int = 2**63
+ point.setX(large_int)
+ self.assertEqual(round(point.x()), large_int)
+
if __name__ == '__main__':
unittest.main()
def testScopeEnd(self):
ref = None
+
def scope():
+
class Ext(Point):
pass
- o = Ext()
+
+ o = Ext() # noqa: F841
global ref
ref = weakref.ref(Ext, self.callback)
+
scope()
gc.collect()
self.assertTrue(self.called)
def testDeleteType(self):
class Ext(Point):
pass
- ref = weakref.ref(Ext, self.callback)
+ ref = weakref.ref(Ext, self.callback) # noqa: F841
del Ext
gc.collect()
self.assertTrue(self.called)
if __name__ == '__main__':
unittest.main()
-
init_paths()
from sample import ObjectType
+
class TestTypeDestructorDoubleFree(unittest.TestCase):
def testTypeDestructorDoubleFree(self):
'''Causes the type destructors of two derived classes to be called.'''
obj = ExtObj1()
child = ObjectType(parent=obj)
self.assertEqual(obj.takeChild(child), child)
+
class ExtObj2(ObjectType):
def __init__(self):
ObjectType.__init__(self)
+
obj = ExtObj2()
child = ObjectType(parent=obj)
self.assertEqual(obj.takeChild(child), child)
scope()
+
if __name__ == '__main__':
unittest.main()
</value-type>
<value-type name="ClassWithFunctionPointer">
- <suppress-warning text="^skipping function 'void ClassWithFunctionPointer::callFunctionPointer.*$" />
+ <suppress-warning text="^skipping public function 'void ClassWithFunctionPointer::callFunctionPointer.*$" />
</value-type>
<value-type name="IntArray" generate="no"/>
<modify-function signature="hideFunction(HideType*)" remove="all"/>
<modify-field name="toBeRenamedField" rename="renamedField"/>
<modify-field name="readOnlyField" write="false"/>
+ <modify-function signature="virtualWithOutParameter(int&)const">
+ <inject-code class="shell" position="override">
+ x = virtualWithOutParameterPyOverride(gil, pyOverride.object());
+ return;
+ </inject-code>
+ </modify-function>
+ <add-function signature="virtualWithOutParameterPyOverride()"
+ return-type="int" python-override="true"/>
</object-type>
<object-type name="Derived" polymorphic-id-expression="%1->type() == Derived::TpDerived">
// CHECKTYPE and ISCONVERTIBLE are used here for test purposes, don't change them.
if (!%CHECKTYPE[ObjectTypeLayout*](layout) && !%ISCONVERTIBLE[ObjectTypeLayout*](layout))
return;
- // %CHECKTYPE[ObjectTypeLayout*](layout)
- // %ISCONVERTIBLE[ObjectTypeLayout*](layout)
+ /* %CHECKTYPE[ObjectTypeLayout*](layout) */
+ /* %ISCONVERTIBLE[ObjectTypeLayout*](layout) */
ObjectTypeLayout* var;
var = %CONVERTTOCPP[ObjectTypeLayout*](layout);
// TODO-CONVERTER: erase this
<suppress-warning text="Shadowing: MDerived2::castToBase3() and MDerived3::castToBase3()" />
<suppress-warning text="Visibility of function 'publicMethod' modified in class 'MDerived1'" />
- <suppress-warning text="^skipping function 'std::enable_if.*ComparisonTester::operator[!=]=.*ComparisonTester.*$"/>
+ <suppress-warning text="^skipping public function 'std::enable_if.*ComparisonTester::operator[!=]=.*ComparisonTester.*$"/>
</typesystem>
from shiboken_paths import init_paths
init_paths()
-from sample import (ValueWithUnitUser, ValueWithUnitDoubleInch,
- ValueWithUnitDoubleMillimeter)
+from sample import ValueWithUnitUser, ValueWithUnitDoubleInch
class TypeSysTypeDefTest(unittest.TestCase):
from sample import ObjectType
+
class DerivedObjectType(ObjectType):
def isPython(self):
return True
def createChild(self, parent):
return DerivedObjectType(parent)
+
class ParentTest(unittest.TestCase):
def testUunsafeParent(self):
o = DerivedObjectType()
o.callVirtualCreateChild()
+
if __name__ == '__main__':
unittest.main()
-
init_paths()
from sample import Size
+
class PointTest(unittest.TestCase):
def testUsingSelfOnCtor(self):
# This is a user added ctor and no errors should happen!
s = Size("3x2")
self.assertEqual(s.height(), 2)
+
if __name__ == '__main__':
unittest.main()
-
from sample import VirtualDtor
+
class ExtendedVirtualDtor(VirtualDtor):
def __init__(self):
VirtualDtor.__init__(self)
+
class VirtualDtorTest(unittest.TestCase):
'''Test case for virtual destructor.'''
if __name__ == '__main__':
unittest.main()
-
from shiboken_paths import init_paths
init_paths()
-from sample import *
+from sample import Point, Str, StrList, VirtualDaughter, VirtualMethods
import warnings
+
class ExtendedVirtualMethods(VirtualMethods):
def __init__(self):
VirtualMethods.__init__(self)
# check if recursion is caused by injected code that calls C++.
return VirtualMethods.recursionOnModifiedVirtual(self, arg) + 10
+
class ExtendedVirtualDaughter(VirtualDaughter):
def __init__(self, name):
VirtualDaughter.__init__(self, name)
self.grand_daughter_name_called = True
return VirtualDaughter.name(self).prepend('Extended')
+
class ExtendedExtendedVirtualDaughter(ExtendedVirtualDaughter):
def __init__(self, name):
ExtendedVirtualDaughter.__init__(self, name)
self.grand_grand_daughter_name_called = True
return ExtendedVirtualDaughter.name(self).prepend('Extended')
+
class VirtualMethodsTest(unittest.TestCase):
'''Test case for virtual methods'''
gc.collect()
def testReimplementedVirtualMethod0(self):
- '''Test Python override of a virtual method with various different parameters is correctly called from C++.'''
+ '''Test Python override of a virtual method with various different parameters
+ is correctly called from C++.'''
vm = VirtualMethods()
evm = ExtendedVirtualMethods()
pt = Point(1.1, 2.2)
obj = ExtendedVirtualMethods()
self.assertRaises(RuntimeWarning, obj.callStrListToStdList, StrList())
+
if __name__ == '__main__':
unittest.main()
-
from shiboken_paths import init_paths
init_paths()
-from sample import *
+from sample import Base1, MDerived1
+
class VisibilityChangeTest(unittest.TestCase):
def testVisibilityChange(self):
b1 = Base1()
- b1.publicMethod() # ok...
+ b1.publicMethod() # ok...
d1 = MDerived1()
- self.assertRaises(TypeError, d1.publicMethod);
+ self.assertRaises(TypeError, d1.publicMethod)
+
if __name__ == '__main__':
unittest.main()
-
-
from sample import VoidHolder, Point
from shiboken6 import Shiboken
+
class VoidHolderTest(unittest.TestCase):
'''Test case for void pointer manipulation.'''
voidholder = VoidHolder()
self.assertEqual(voidholder.voidPointer(), None)
+
if __name__ == '__main__':
unittest.main()
-
def testBasic(self):
'''ObjectType weakref'''
obj = ObjectType()
- ref = weakref.ref(obj, self.cb)
+ ref = weakref.ref(obj, self.cb) # noqa: F841
del obj
# PYSIDE-535: Need to collect garbage in PyPy to trigger deletion
gc.collect()
def testPrivateDtor(self):
'''PrivateDtor weakref'''
obj = PrivateDtor.instance()
- ref = weakref.ref(obj, self.cb)
+ ref = weakref.ref(obj, self.cb) # noqa: F841
del obj
# PYSIDE-535: Need to collect garbage in PyPy to trigger deletion
gc.collect()
from sample import Point
-class ExtPoint(Point): pass
+
+class ExtPoint(Point):
+ pass
+
class TestWritableClassDict(unittest.TestCase):
def testSetattrOnClass(self):
pt = ExtPoint()
self.assertEqual(pt.bar, 321)
+
if __name__ == '__main__':
unittest.main()
self.assertTrue(Shiboken.createdByPython(bb))
bb.disposeObjectType(bb.keepObjectType(obj))
+ def testWrapInstancePreserveId(self):
+ """PYSIDE-31: Verify that wrapInstance() returns the existing wrapper
+ even if a base class type is specified."""
+ v = ObjectView() # inherits ObjectType
+ addresses = Shiboken.getCppPointer(v)
+ self.assertTrue(addresses)
+ address = addresses[0]
+ wrapped = Shiboken.wrapInstance(address, ObjectType)
+ self.assertEqual(id(wrapped), id(v))
+
def testIsOwnedByPython(self):
obj = ObjectType()
self.assertTrue(Shiboken.ownedByPython(obj))
${CMAKE_CURRENT_BINARY_DIR}/smart/stdsharedptrvirtualmethodtester_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/smart/std_shared_ptr_integer_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/smart/std_shared_ptr_int_wrapper.cpp
+${CMAKE_CURRENT_BINARY_DIR}/smart/std_shared_ptr_std_string_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/smart/std_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/smart/std_optional_int_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/smart/std_optional_integer_wrapper.cpp
self.assertTrue(five > four)
self.assertRaises(NotImplementedError,
- lambda : Obj.createNullSharedPtrInteger() == four)
+ lambda: Obj.createNullSharedPtrInteger() == four)
def testSmartPointerObjectComparison(self):
"""Test a pointee class without comparison operators."""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-import gc
import os
import sys
import unittest
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-import gc
import os
import sys
import unittest
self.assertFalse(np)
p = StdSharedPtrTestBench.createInt()
StdSharedPtrTestBench.printInt(p)
+ ip = std.StdIntPtr(42)
+ StdSharedPtrTestBench.printInt(ip)
+
+ def testString(self):
+ np = StdSharedPtrTestBench.createNullString()
+ StdSharedPtrTestBench.printString(np)
+ self.assertFalse(np)
+ p = StdSharedPtrTestBench.createString("bla")
+ StdSharedPtrTestBench.printString(p)
def testVirtuals(self):
"""Test whether code generating virtual function overrides is generated
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-import gc
import os
import sys
import unittest
value-check-method="operator bool"
ref-count-method="use_count"
reset-method="reset"
- instantiations="Integer,int">
+ instantiations="Integer,int=StdIntPtr,std::string">
<include file-name="memory" location="global"/>
</smart-pointer-type>
raise RuntimeError("Error deploying")
suffix = "exe" if sys.platform == "win32" else "bin"
- binary = f"{tmpdirname}/{main_file.stem}.{suffix}"
+
+ if sys.platform != "darwin":
+ binary = f"{tmpdirname}/{main_file.stem}.{suffix}"
+ else:
+ binary = f"{tmpdirname}/pyside_app_demo.app/Contents/MacOS/{main_file.stem}"
+
if run_process([binary]) != 0:
raise RuntimeError("Error running the deployed example")
return True
def is_lts_version(version: list) -> bool:
- return version[0] == 5 or version[1] == 2
+ return version[0] == 5 or version[1] in (2, 5)
def version_tag(version: list) -> str:
# For major/minor releases, skip all fixes with "Pick-to: " since they
# appear in bug-fix releases.
if args.type != "bug-fix":
- args.exclude = True
+ args.exclude = True
print(f'Assuming "{args.type}" version', file=sys.stderr)
if args.type not in ("bug-fix", "minor", "major"):
# the tag number does not matter much since we update the sdk later
DEFAULT_SDK_TAG = 6514223
-ANDROID_NDK_VERSION = "25c"
+ANDROID_NDK_VERSION = "26b"
def run_command(command: List[str], cwd: str = None, ignore_fail: bool = False,
download_android_ndk, install_android_packages)
# Note: Does not work with PyEnv. Your Host Python should contain openssl.
-PYTHON_VERSION = "3.10"
+PYTHON_VERSION = "3.11"
SKIP_UPDATE_HELP = ("skip the updation of SDK packages build-tools, platform-tools to"
" latest version")
parser.add_argument("-v", "--verbose", help="run in verbose mode", action="store_const",
dest="loglevel", const=logging.INFO)
- parser.add_argument("--api-level", type=str, default="31", help="Android API level to use")
+ parser.add_argument("--api-level", type=str, default="33", help="Android API level to use")
parser.add_argument("--ndk-path", type=str, help="Path to Android NDK (Preferred r25c)")
# sdk path is needed to compile all the Qt Java Acitivity files into Qt6AndroidBindings.jar
parser.add_argument("--sdk-path", type=str, help="Path to Android SDK")
ndk_path=ndk_path,
api_level=platform_data.api_level,
android_py_install_path_prefix=pyside6_deploy_cache,
+ host_python_path=sys.executable
)
logging.info(f"Writing Python cross compile script into {python_ccompile_script}")
# run the cross compile script
logging.info(f"Running Qt for Python cross-compile for platform {platform_data.plat_name}")
qfp_ccompile_cmd = [sys.executable, "setup.py", "bdist_wheel", "--parallel=9",
- "--standalone", "--limited-api=yes",
+ "--standalone",
f"--cmake-toolchain-file={str(qfp_toolchain.resolve())}",
f"--qt-host-path={qt_install_path}/gcc_64",
f"--plat-name=android_{platform_data.plat_name}",
f"--python-target-path={python_path}",
(f"--qt-target-path={qt_install_path}/"
f"android_{platform_data.qt_plat_name}"),
- "--no-qt-tools", "--unity"]
+ "--no-qt-tools"]
run_command(qfp_ccompile_cmd, cwd=pyside_setup_dir, dry_run=dry_run, show_stdout=True)
export LD=$TOOLCHAIN/ld
export READELF=$TOOLCHAIN/llvm-readelf
export CFLAGS='-fPIC -DANDROID'
-./configure --host=$HOST_ARCH --target=$HOST_ARCH --build=x86_64-pc-linux-gnu --enable-shared \
+./configure --host=$HOST_ARCH --target=$HOST_ARCH --build=x86_64-pc-linux-gnu \
+--with-build-python={{ host_python_path }} --enable-shared \
--enable-ipv6 ac_cv_file__dev_ptmx=yes ac_cv_file__dev_ptc=no --without-ensurepip \
ac_cv_little_endian_double=yes
make BLDSHARED="$CC -shared" CROSS-COMPILE=$TOOL_PREFIX- CROSS_COMPILE_TARGET=yes
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
# toolchain file to cross compile Qt for Python wheels for Android
-cmake_minimum_required(VERSION 3.18)
+cmake_minimum_required(VERSION 3.23)
include_guard(GLOBAL)
set(CMAKE_SYSTEM_NAME Android)
{% if plat_name == "armv7a" -%}
import sys
from argparse import ArgumentParser, RawTextHelpFormatter
from dataclasses import dataclass
-from enum import Enum
+from enum import IntEnum, Enum
from pathlib import Path
from textwrap import dedent
MD = 1
+class ModuleType(IntEnum):
+ ESSENTIALS = 0
+ ADDONS = 1
+ M2M = 2
+
+
SUFFIXES = {Format.RST: "rst", Format.MD: "md"}
return i.suffix in IMAGE_SUFFIXES
+@dataclass
+class ModuleDescription:
+ """Specifies a sort key and type for a Qt module."""
+ sort_key: int = 0
+ module_type: ModuleType = ModuleType.ESSENTIALS
+ description: str = ''
+
+
+MODULE_DESCRIPTIONS = {
+ "async": ModuleDescription(16, ModuleType.ESSENTIALS, ''),
+ "corelib": ModuleDescription(15, ModuleType.ESSENTIALS, ''),
+ "dbus": ModuleDescription(22, ModuleType.ESSENTIALS, ''),
+ "designer": ModuleDescription(11, ModuleType.ESSENTIALS, ''),
+ "gui": ModuleDescription(25, ModuleType.ESSENTIALS, ''),
+ "network": ModuleDescription(20, ModuleType.ESSENTIALS, ''),
+ "opengl": ModuleDescription(26, ModuleType.ESSENTIALS, ''),
+ "qml": ModuleDescription(0, ModuleType.ESSENTIALS, ''),
+ "quick": ModuleDescription(1, ModuleType.ESSENTIALS, ''),
+ "quickcontrols": ModuleDescription(2, ModuleType.ESSENTIALS, ''),
+ "samplebinding": ModuleDescription(30, ModuleType.ESSENTIALS, ''),
+ "scriptableapplication": ModuleDescription(30, ModuleType.ESSENTIALS, ''),
+ "sql": ModuleDescription(21, ModuleType.ESSENTIALS, ''),
+ "uitools": ModuleDescription(12, ModuleType.ESSENTIALS, ''),
+ "widgetbinding": ModuleDescription(30, ModuleType.ESSENTIALS, ''),
+ "widgets": ModuleDescription(10, ModuleType.ESSENTIALS, ''),
+ "xml": ModuleDescription(24, ModuleType.ESSENTIALS, ''),
+ "Qt Demos": ModuleDescription(0, ModuleType.ADDONS, ''), # from Qt repos
+ "3d": ModuleDescription(30, ModuleType.ADDONS, ''),
+ "axcontainer": ModuleDescription(20, ModuleType.ADDONS, ''),
+ "bluetooth": ModuleDescription(20, ModuleType.ADDONS, ''),
+ "charts": ModuleDescription(12, ModuleType.ADDONS, ''),
+ "datavisualization": ModuleDescription(11, ModuleType.ADDONS, ''),
+ "demos": ModuleDescription(0, ModuleType.ADDONS, ''),
+ "external": ModuleDescription(20, ModuleType.ADDONS, ''),
+ "graphs": ModuleDescription(10, ModuleType.ADDONS, ''),
+ "httpserver": ModuleDescription(0, ModuleType.ADDONS, ''),
+ "location": ModuleDescription(20, ModuleType.ADDONS, ''),
+ "multimedia": ModuleDescription(12, ModuleType.ADDONS, ''),
+ "networkauth": ModuleDescription(20, ModuleType.ADDONS, ''),
+ "pdf": ModuleDescription(20, ModuleType.ADDONS, ''),
+ "pdfwidgets": ModuleDescription(20, ModuleType.ADDONS, ''),
+ "quick3d": ModuleDescription(20, ModuleType.ADDONS, ''),
+ "remoteobjects": ModuleDescription(20, ModuleType.ADDONS, ''),
+ "serialbus": ModuleDescription(30, ModuleType.ADDONS, ''),
+ "serialport": ModuleDescription(30, ModuleType.ADDONS, ''),
+ "spatialaudio": ModuleDescription(20, ModuleType.ADDONS, ''),
+ "speech": ModuleDescription(20, ModuleType.ADDONS, ''),
+ "statemachine": ModuleDescription(30, ModuleType.ADDONS, ''),
+ "webchannel": ModuleDescription(30, ModuleType.ADDONS, ''),
+ "webenginequick": ModuleDescription(15, ModuleType.ADDONS, ''),
+ "webenginewidgets": ModuleDescription(16, ModuleType.ADDONS, ''),
+ "coap": ModuleDescription(0, ModuleType.M2M, ''),
+ "mqtt": ModuleDescription(0, ModuleType.M2M, ''),
+ "opcua": ModuleDescription(0, ModuleType.M2M, '')
+}
+
+
+def module_sort_key(name):
+ """Return key for sorting modules."""
+ description = MODULE_DESCRIPTIONS.get(name)
+ module_type = int(description.module_type) if description else 5
+ sort_key = description.sort_key if description else 100
+ return f"{module_type}:{sort_key:04}:{name}"
+
+
+def module_title(name):
+ """Return title for a module."""
+ result = name.title()
+ description = MODULE_DESCRIPTIONS.get(name)
+ if description:
+ if description.description:
+ result += " - " + description.description
+ if description.module_type == ModuleType.M2M:
+ result += " (M2M)"
+ elif description.module_type == ModuleType.ADDONS:
+ result += " (Add-ons)"
+ else:
+ result += " (Essentials)"
+ return result
+
+
@dataclass
class ExampleData:
"""Example data for formatting the gallery."""
return (p.module_name, result)
+def example_sort_key(example: ExampleData):
+ name = example.example
+ return "AAA" + name if "gallery" in name else name
+
+
def sort_examples(example):
result = {}
for module in example.keys():
- result[module] = sorted(example.get(module), key=lambda e: e.doc_file)
+ result[module] = sorted(example.get(module), key=example_sort_key)
return result
shutil.rmtree(EXAMPLES_DOC, ignore_errors=True)
if not opt_quiet:
print("WARNING: Deleted old html directory")
- EXAMPLES_DOC.mkdir()
+ EXAMPLES_DOC.mkdir(exist_ok=True)
scan_examples_dir(EXAMPLES_DIR)
if options.qt_src_dir:
index_files = []
with open(f"{EXAMPLES_DOC}/index.rst", "w") as f:
f.write(BASE_CONTENT)
- for module_name, e in sorted(examples.items()):
+ for module_name in sorted(examples.keys(), key=module_sort_key):
+ e = examples.get(module_name)
for i in e:
index_files.append(i.doc_file)
- f.write(f"{module_name.title()}\n")
- f.write(f"{'*' * len(module_name.title())}\n")
+ title = module_title(module_name)
+ f.write(f"{title}\n")
+ f.write(f"{'*' * len(title)}\n")
f.write(get_module_gallery(e))
f.write("\n\n")
f.write(footer_index)
"QAccessibleEvent",
"QAccessibleInterface",
"QAccessibleObject",
+ "QAccessibleSelectionInterface",
"QAccessibleStateChangeEvent",
"QAccessibleTableCellInterface",
"QAccessibleTableModelChangeEvent",
description = PROJECT_DESCRIPTION
readme = PROJECT_README
dynamic = ["version"]
-requires-python = ">=3.8, <3.13"
+requires-python = ">=3.9, <3.13"
keywords = ["Qt"]
license = {text = "LGPL"}
dependencies = PROJECT_DEPENDENCIES
"Programming Language :: C++",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
PROJECT_SCRIPTS
[tool.distutils.bdist_wheel]
-py_limited_api = "cp38"
+py_limited_api = "cp39"
plat_name = PROJECT_TAG
[tool.setuptools.dynamic]